From cd3c68939c7b4bf3b2e2863d6b8d0921dbc63bb1 Mon Sep 17 00:00:00 2001 From: Jeremiah Zucker Date: Tue, 5 Dec 2023 12:28:28 -0500 Subject: [PATCH] Sync Android and JVM packages to latest (#222) * SetTimeoutPlugin exception handling enhancement pt. 1 * Make views in Flow nullable * lint * SetTimeoutPlugin exception handling enhancement pt. 2 * Call state hook when releasing player * lint * expose V8 params in psuedo constructors * Update into extension method to handle null view case * support null contextual serialization for assets * viewmodel tests * fix test * attempt fix 2 * guard against logging on release due to weird timing * add error handling during asset expansion * clear guideline for top level render function * add test for hydration scope cancelling on player release * add coroutine-test import to android player package * lint * M1 J2V8 Support * fix build file * replace deprecated channel method * fix bazel intellij sync * [android#265]: fix orientation change reset * [android#269]: PlayerViewModel::runtime null safety * [jvm#224]: hooks@0.15.0 * [android#276]: asset handler hook * [jvm#229]: replacing `getFunction` with `getInvokable` * [jvm#230]: automatic deserializes functions and invokables * [jvm#231]: name loggers dynamically * [jvm#228]: migrate to NodeSerializableField * fix lingering getFunction replacements * [jvm#235]: local class discriminators * [jvm#239]: `JSErrorException` enhancement * [jvm#237]: expose skipTransition hook * [jvm#240]: fix testutils node * [jvm#241]: `NavigationFlowEndState.param` helper * [android#287]: scroll to top when navigating to a new view * [jvm#238]: dedicated runtime thread * [android#286]: `SuspendableAsset` * [jvm#243] debuggable v8 * [android#292] android debuggable v8 * upgrade kotlin to 1.7 and lint * working demo app * all //jvm tests passing * fix all android tests * ensure test target fails on failure * fix instrumented android tests * lint * bootstrap intents break everything * androidx orchestrator * clean up workspace and use released player rules for kotlin upgrade * format and comment androidtest python script * pr comments --------- Co-authored-by: Larry --- .circleci/config.yml | 13 + .editorconfig | 2 - WORKSPACE | 52 +- android/build.bzl | 6 +- android/demo/BUILD | 3 + android/demo/deps.bzl | 5 +- android/demo/scripts/androidtest.sh | 79 +- .../reference/demo/test/MainActivityTest.kt | 7 +- .../reference/demo/test/SplashActivityTest.kt | 6 +- .../demo/test/assets/action/ActionUITest.kt | 21 +- .../assets/collection/CollectionUITest.kt | 12 +- .../demo/test/assets/info/InfoUITest.kt | 10 +- .../demo/test/assets/input/InputUITest.kt | 98 +- .../demo/test/assets/text/TextUITest.kt | 14 +- .../demo/test/base/ApplitoolsTest.kt | 22 +- .../base/{assertions.kt => Assertions.kt} | 2 +- .../reference/demo/test/base/AssetUITest.kt | 13 +- .../reference/demo/test/base/Matchers.kt | 58 + .../demo/test/base/PerformanceTest.kt | 6 +- .../fragment/PlayerFragmentScrollingTest.kt | 20 + .../assets/mocks/misc/long-multi-view.json | 73 + .../demo/lifecycle/DemoPlayerViewModel.kt | 5 + .../android/reference/demo/model/AssetMock.kt | 2 +- .../reference/demo/model/StringMock.kt | 2 +- .../demo/ui/base/BasePlayerFragment.kt | 47 +- .../reference/demo/ui/main/MainActivity.kt | 30 +- .../reference/demo/ui/main/MainViewModel.kt | 15 +- .../reference/demo/ui/start/StartFragment.kt | 19 +- android/player/BUILD | 4 +- android/player/api/player.api | 4 +- android/player/deps.bzl | 3 +- .../player/android/AssetContextAndroidTest.kt | 1 + .../intuit/player/android/AndroidPlayer.kt | 87 +- .../com/intuit/player/android/AssetContext.kt | 18 +- .../player/android/asset/DecodableAsset.kt | 9 +- .../player/android/asset/RenderableAsset.kt | 65 +- .../player/android/asset/SuspendableAsset.kt | 131 ++ .../debug/UnsupportedScriptProvider.kt | 14 + .../android/extensions/{into.kt => Into.kt} | 19 +- .../{overlayStyles.kt => OverlayStyles.kt} | 4 +- .../{removeSelf.kt => RemoveSelf.kt} | 0 .../android/lifecycle/ManagedPlayerState.kt | 4 + .../android/lifecycle/PlayerViewModel.kt | 91 +- .../player/android/registry/RegistryPlugin.kt | 7 +- .../player/android/ui/PlayerFragment.kt | 114 +- android/player/src/main/res/values/tags.xml | 4 + .../player/src/test/java/android/util/Log.kt | 51 +- .../player/android/AndroidPlayerTest.kt | 27 +- .../intuit/player/android/AssetContextTest.kt | 17 + .../CoroutineTestDispatcherExtension.kt | 20 + .../player/android/extensions/IntoKtTest.kt | 113 + .../android/lifecycle/PlayerViewModelTest.kt | 262 +++ .../android/logger/AndroidLoggerTest.kt | 22 +- .../renderer/BaseRenderableAssetTest.kt | 16 +- .../android/renderer/BrokenAssetTest.kt | 77 + .../android/renderer/HydrationScopeTest.kt | 48 +- .../android/renderer/NestedAssetTest.kt | 19 +- .../android/renderer/SimpleAssetTest.kt | 37 +- .../player/android/utils/BrokenAsset.kt | 61 + .../player/android/utils/{json.kt => Json.kt} | 0 .../player/android/utils/NestedAsset.kt | 21 +- .../player/android/utils/OtherSimpleAsset.kt | 12 +- .../player/android/utils/SimpleAsset.kt | 7 +- .../player/android/utils/TestAssetsPlugin.kt | 5 +- .../android/utils/{updates.kt => Updates.kt} | 11 +- jvm/BUILD | 8 +- jvm/core/api/core.api | 1814 +++++++++-------- .../com/intuit/player/jvm/core/asset/Asset.kt | 16 +- .../player/jvm/core/asset/AssetWrapper.kt | 6 +- .../intuit/player/jvm/core/asset/MetaData.kt | 16 +- .../player/jvm/core/bridge/Invokable.kt | 35 +- .../bridge/{isUndefined.kt => IsUndefined.kt} | 0 .../jvm/core/bridge/JSErrorException.kt | 16 +- .../com/intuit/player/jvm/core/bridge/Node.kt | 58 + .../intuit/player/jvm/core/bridge/Promise.kt | 56 +- .../jvm/core/bridge/global/JSIterator.kt | 3 +- .../player/jvm/core/bridge/global/JSMap.kt | 5 +- .../player/jvm/core/bridge/hooks/NodeHook.kt | 18 +- .../core/bridge/hooks/NodeSyncBailHook1.kt | 36 + .../jvm/core/bridge/hooks/NodeSyncHook.kt | 17 +- .../bridge/hooks/NodeSyncWaterfallHook.kt | 18 +- .../bridge/runtime/PlayerRuntimeConfig.kt | 7 +- .../bridge/runtime/PlayerRuntimeFactory.kt | 4 +- .../player/jvm/core/bridge/runtime/Runtime.kt | 17 + .../encoding/AbstractRuntimeValueDecoders.kt | 12 +- .../serialization/encoding/NodeDecoders.kt | 3 +- .../serialization/format/RuntimeFormat.kt | 9 +- .../json/{pretty.kt => Pretty.kt} | 0 .../serializers/{defer.kt => Defer.kt} | 0 .../serializers/FunctionSerializers.kt | 117 +- .../serializers/GenericSerializer.kt | 15 +- .../serializers/{module.kt => Module.kt} | 15 +- .../serializers/NodeSerializableField.kt | 116 +- .../serializers/NodeSerializers.kt | 8 +- .../serializers/ThrowableSerializer.kt | 118 +- .../player/jvm/core/data/DataController.kt | 7 +- .../jvm/core/data/DataModelWithParser.kt | 5 +- .../experimental/ExperimentalPlayerApi.kt | 2 +- .../player/jvm/core/expressions/Expression.kt | 11 +- .../core/expressions/ExpressionEvaluator.kt | 3 +- .../com/intuit/player/jvm/core/flow/Flow.kt | 4 +- .../player/jvm/core/flow/FlowController.kt | 11 +- .../player/jvm/core/flow/FlowInstance.kt | 44 +- .../intuit/player/jvm/core/flow/FlowResult.kt | 10 +- .../intuit/player/jvm/core/flow/Navigation.kt | 2 +- .../player/jvm/core/flow/NavigationFlow.kt | 2 +- .../player/jvm/core/flow/TransitionOptions.kt | 2 +- .../core/flow/state/NavigationFlowState.kt | 56 +- .../flow/state/NavigationFlowStateType.kt | 2 +- .../player/jvm/core/logger/TapableLogger.kt | 27 +- .../jvm/core/managed/AsyncIterationManager.kt | 13 +- .../player/jvm/core/managed/AsyncIterator.kt | 2 - .../player/jvm/core/player/HeadlessPlayer.kt | 110 +- .../player/jvm/core/player/JSPlayerConfig.kt | 16 +- .../intuit/player/jvm/core/player/Player.kt | 48 +- .../jvm/core/player/PlayerCompletable.kt | 5 +- .../jvm/core/player/PlayerFlowStatus.kt | 14 +- .../player/jvm/core/player/PlayerHooks.kt | 2 +- .../jvm/core/player/state/NamedState.kt | 8 +- .../jvm/core/player/state/PlayerFlowState.kt | 82 +- .../jvm/core/plugins/JSPluginWrapper.kt | 2 +- .../jvm/core/plugins/JSScriptPluginWrapper.kt | 14 +- .../player/jvm/core/plugins/LoggerPlugin.kt | 5 +- .../player/jvm/core/plugins/RuntimePlugin.kt | 1 + .../plugins/logging/PlayerLoggingConfig.kt | 4 +- .../plugins/logging/PlayerLoggingFactory.kt | 7 +- .../jvm/core/resolver/ResolveOptions.kt | 4 +- .../player/jvm/core/resolver/Resolver.kt | 9 +- .../intuit/player/jvm/core/utils/Future.kt | 13 + .../jvm/core/validation/BindingInstance.kt | 5 +- .../player/jvm/core/validation/Validation.kt | 4 +- .../core/validation/ValidationController.kt | 6 +- .../jvm/core/validation/ValidationInfo.kt | 2 +- .../jvm/core/validation/ValidationResponse.kt | 34 +- .../com/intuit/player/jvm/core/view/View.kt | 6 +- .../player/jvm/core/view/ViewController.kt | 9 +- .../jvm/core/view/ViewControllerHooks.kt | 2 +- .../intuit/player/jvm/core/view/ViewHooks.kt | 12 +- .../intuit/player/jvm/core/asset/AssetTest.kt | 20 +- .../player/jvm/core/bridge/JsonPromiseTest.kt | 20 +- .../jvm/core/bridge/NodeGetSymbolTest.kt | 64 + .../player/jvm/core/bridge/PromiseTest.kt | 38 +- .../jvm/core/bridge/global/JSMapTest.kt | 11 +- .../core/bridge/hooks/NodeSyncBailHookTest.kt | 105 + .../jvm/core/bridge/hooks/NodeSyncHookTest.kt | 10 +- .../bridge/hooks/NodeSyncWaterfallHookTest.kt | 12 +- .../bridge/runtime/RuntimeBlockingTest.kt | 150 ++ .../serializers/NodeSerializableFieldTest.kt | 173 ++ .../jvm/core/data/DataControllerTest.kt | 13 +- .../ExpressionSerializationTest.kt | 10 +- .../jvm/core/expressions/ExpressionTest.kt | 9 +- .../flow/FlowControllerIntegrationTest.kt | 2 +- .../jvm/core/flow/FlowControllerTest.kt | 3 +- .../core/managed/AsyncIterationManagerTest.kt | 8 +- .../jvm/core/player/HeadlessPlayerTest.kt | 71 +- .../player/jvm/core/player/PlayerHooksTest.kt | 32 +- .../core/player/state/CompletedStateTest.kt | 28 +- .../jvm/core/player/state/ErrorStateTest.kt | 24 +- .../core/player/state/InProgressStateTest.kt | 29 +- .../core/player/state/NotStartedStateTest.kt | 11 +- .../player/jvm/core/plugins/PluggableTest.kt | 6 +- .../jvm/core/serialization/DecodingTests.kt | 13 +- .../jvm/core/serialization/EncodingTests.kt | 14 +- .../serialization/NodeSerializationTest.kt | 33 +- .../jvm/core/view/ViewControllerHooksTest.kt | 20 +- .../jvm/core/view/ViewControllerTest.kt | 3 + .../player/jvm/core/view/ViewHooksTest.kt | 33 +- .../intuit/player/jvm/core/view/ViewTest.kt | 15 +- jvm/dependencies/versions.bzl | 10 +- .../player/jvm/graaljs/bridge/GraalNode.kt | 16 +- .../graaljs/bridge/runtime/GraalRuntime.kt | 28 +- .../serialization/encoding/GraalDecoders.kt | 22 +- .../serialization/encoding/GraalEncoders.kt | 70 +- .../serialization/format/GraalFormat.kt | 4 +- .../{handleValue.kt => HandleValue.kt} | 60 +- .../graaljs/extensions/{lock.kt => Lock.kt} | 2 +- .../extensions/{unwrap.kt => Unwrap.kt} | 0 .../player/jvm/graaljs/base/GraalTest.kt | 21 +- .../jvm/graaljs/bridge/GraalNodeTest.kt | 65 +- .../graaljs/bridge/format/GraalFormatTest.kt | 13 +- .../bridge/serialization/DecodingTest.kt | 10 +- .../bridge/serialization/EncodingTest.kt | 6 +- .../bridge/serialization/JsonEncodingTests.kt | 2 +- .../jvm/graaljs/promise/GraalPromiseTest.kt | 4 +- jvm/j2v8/api/j2v8.api | 7 + jvm/j2v8/deps.bzl | 13 +- jvm/j2v8/libs/BUILD | 6 +- .../com/intuit/player/jvm/j2v8/V8Primitive.kt | 21 +- .../intuit/player/jvm/j2v8/bridge/V8Node.kt | 48 +- .../jvm/j2v8/bridge/runtime/V8Runtime.kt | 131 +- .../serialization/encoding/V8Decoders.kt | 44 +- .../serialization/encoding/V8Encoders.kt | 125 +- .../bridge/serialization/format/J2V8Format.kt | 11 +- .../jvm/j2v8/extensions/{args.kt => Args.kt} | 0 .../player/jvm/j2v8/extensions/HandleValue.kt | 77 + .../j2v8/extensions/{invoke.kt => Invoke.kt} | 4 +- .../intuit/player/jvm/j2v8/extensions/Lock.kt | 50 + .../jvm/j2v8/extensions/{map.kt => Map.kt} | 0 .../j2v8/extensions/{unlock.kt => Unlock.kt} | 0 .../j2v8/extensions/{unwrap.kt => Unwrap.kt} | 0 .../player/jvm/j2v8/extensions/handleValue.kt | 58 - .../intuit/player/jvm/j2v8/extensions/lock.kt | 43 - .../jvm/j2v8/plugins/V8ScriptPlayerPlugin.kt | 6 +- .../jvm/j2v8/base/AutoAcquireJ2V8Test.kt | 17 - .../intuit/player/jvm/j2v8/base/J2V8Test.kt | 33 +- .../player/jvm/j2v8/bridge/V8NodeTest.kt | 89 +- .../bridge/serialization/V8DecoderTest.kt | 102 +- .../bridge/serialization/V8EncoderTest.kt | 34 +- .../serialization/encoding/DecodingTests.kt | 36 +- .../serialization/encoding/EncodingTests.kt | 27 +- .../encoding/JsonEncodingTests.kt | 24 +- .../serialization/format/J2V8FormatTest.kt | 29 +- .../serializers/ThrowableSerializerTest.kt | 25 +- .../player/jvm/j2v8/extensions/InvokeTest.kt | 18 +- .../player/jvm/j2v8/extensions/UnlockTest.kt | 14 +- .../player/jvm/j2v8/promise/V8PromiseTest.kt | 8 +- .../intuit/player/jvm/perf/jmh/PlayerPerf.kt | 16 +- .../player/jvm/perf/junit/RuntimePerfTest.kt | 7 +- .../com/intuit/player/jvm/testutils/Node.kt | 11 +- .../player/jvm/utils/test/PlayerTest.kt | 2 +- .../player/jvm/utils/test/PromiseUtils.kt | 4 +- ...{runBlockingTest.kt => RunBlockingTest.kt} | 0 .../player/jvm/utils/test/RuntimeTest.kt | 12 +- .../utils/test/{testMocks.kt => TestMocks.kt} | 0 .../player/jvm/utils/{json.kt => Json.kt} | 8 +- .../jvm/utils/{makeFlow.kt => MakeFlow.kt} | 3 +- .../player/jvm/utils/{player.kt => Player.kt} | 0 .../player/jvm/utils/StackTraceElements.kt | 15 + .../player/jvm/utils/mocks/ClassLoaderMock.kt | 2 +- .../intuit/player/jvm/utils/MakeFlowTests.kt | 4 +- .../player/plugins/beacon/BeaconPlugin.kt | 24 +- .../player/plugins/beacon/BeaconPluginTest.kt | 8 +- .../jvm/src/main/kotlin/CheckPathPlugin.kt | 3 +- .../plugins/coroutines/FlowScopePlugin.kt | 15 +- .../plugins/coroutines/UpdatesPlugin.kt | 14 +- .../plugins/coroutines/FlowScopePluginTest.kt | 39 +- .../plugins/expression/ExpressionPlugin.kt | 12 +- .../expression/ExpressionPluginTest.kt | 9 +- .../externalAction/ExternalActionPlugin.kt | 7 +- .../ExternalActionPluginTest.kt | 6 +- .../plugins/logging/jul/JavaLoggerPlugin.kt | 2 +- .../logging/jul/JavaLoggerPluginTest.kt | 2 +- .../metrics/{metrics.kt => Metrics.kt} | 27 +- .../jvm/plugins/metrics/MetricsPlugin.kt | 22 +- .../jvm/plugins/metrics/MetricsPluginTest.kt | 2 +- .../player/plugins/pubsub/PubSubPlugin.kt | 12 +- .../player/plugins/pubsub/PubSubPluginTest.kt | 10 +- .../reference/assets/action/ActionTest.kt | 3 + .../android/reference/assets/info/InfoTest.kt | 3 +- .../reference/assets/input/InputTest.kt | 1 + .../test/{assertions.kt => Assertions.kt} | 17 +- .../reference/assets/test/AssetTest.kt | 93 +- .../android/reference/assets/test/BUILD | 11 +- .../android/reference/assets/test/deps.bzl | 1 + .../android/reference/assets/action/Action.kt | 25 +- .../reference/assets/collection/Collection.kt | 10 +- .../android/reference/assets/info/Info.kt | 8 +- .../android/reference/assets/input/Input.kt | 66 +- .../android/reference/assets/text/Text.kt | 44 +- plugins/set-time-out/jvm/api/set-time-out.api | 12 +- .../plugins/settimeout/SetTimeoutPlugin.kt | 36 +- .../SetTimeoutPluginTest.kt | 128 ++ .../logging/slf4j/Slf4jLoggerPlugin.kt | 2 +- .../logging/slf4j/Slf4jLoggerPluginTest.kt | 2 +- 264 files changed, 5807 insertions(+), 2603 deletions(-) delete mode 100644 .editorconfig rename android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/{assertions.kt => Assertions.kt} (97%) create mode 100644 android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/Matchers.kt create mode 100644 android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/fragment/PlayerFragmentScrollingTest.kt create mode 100644 android/demo/src/main/assets/mocks/misc/long-multi-view.json create mode 100644 android/player/src/main/java/com/intuit/player/android/asset/SuspendableAsset.kt create mode 100644 android/player/src/main/java/com/intuit/player/android/debug/UnsupportedScriptProvider.kt rename android/player/src/main/java/com/intuit/player/android/extensions/{into.kt => Into.kt} (84%) rename android/player/src/main/java/com/intuit/player/android/extensions/{overlayStyles.kt => OverlayStyles.kt} (93%) rename android/player/src/main/java/com/intuit/player/android/extensions/{removeSelf.kt => RemoveSelf.kt} (100%) create mode 100644 android/player/src/main/res/values/tags.xml create mode 100644 android/player/src/test/java/com/intuit/player/android/extensions/CoroutineTestDispatcherExtension.kt create mode 100644 android/player/src/test/java/com/intuit/player/android/extensions/IntoKtTest.kt create mode 100644 android/player/src/test/java/com/intuit/player/android/lifecycle/PlayerViewModelTest.kt create mode 100644 android/player/src/test/java/com/intuit/player/android/renderer/BrokenAssetTest.kt create mode 100644 android/player/src/test/java/com/intuit/player/android/utils/BrokenAsset.kt rename android/player/src/test/java/com/intuit/player/android/utils/{json.kt => Json.kt} (100%) rename android/player/src/test/java/com/intuit/player/android/utils/{updates.kt => Updates.kt} (84%) rename jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/{isUndefined.kt => IsUndefined.kt} (100%) create mode 100644 jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHook1.kt rename jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/json/{pretty.kt => Pretty.kt} (100%) rename jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/{defer.kt => Defer.kt} (100%) rename jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/{module.kt => Module.kt} (61%) create mode 100644 jvm/core/src/main/kotlin/com/intuit/player/jvm/core/utils/Future.kt create mode 100644 jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/NodeGetSymbolTest.kt create mode 100644 jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHookTest.kt create mode 100644 jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/runtime/RuntimeBlockingTest.kt create mode 100644 jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableFieldTest.kt rename jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/{handleValue.kt => HandleValue.kt} (52%) rename jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/{lock.kt => Lock.kt} (99%) rename jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/{unwrap.kt => Unwrap.kt} (100%) rename jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/{args.kt => Args.kt} (100%) create mode 100644 jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/HandleValue.kt rename jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/{invoke.kt => Invoke.kt} (67%) create mode 100644 jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Lock.kt rename jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/{map.kt => Map.kt} (100%) rename jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/{unlock.kt => Unlock.kt} (100%) rename jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/{unwrap.kt => Unwrap.kt} (100%) delete mode 100644 jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/handleValue.kt delete mode 100644 jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/lock.kt delete mode 100644 jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/AutoAcquireJ2V8Test.kt rename jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/{runBlockingTest.kt => RunBlockingTest.kt} (100%) rename jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/{testMocks.kt => TestMocks.kt} (100%) rename jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/{json.kt => Json.kt} (82%) rename jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/{makeFlow.kt => MakeFlow.kt} (90%) rename jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/{player.kt => Player.kt} (100%) create mode 100644 jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/StackTraceElements.kt rename plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/{metrics.kt => Metrics.kt} (69%) rename plugins/reference-assets/android/src/androidTest/java/com/intuit/player/android/reference/assets/test/{assertions.kt => Assertions.kt} (73%) create mode 100644 plugins/set-time-out/jvm/src/test/kotlin/com.intuit.player.plugins.settimeout/SetTimeoutPluginTest.kt diff --git a/.circleci/config.yml b/.circleci/config.yml index 2e089597a..56108e852 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -249,6 +249,19 @@ jobs: - run: bazel test --define=APPLITOOLS_API_KEY=${APPLITOOLS_API_KEY} --define=APPLITOOLS_BATCH_ID=${CIRCLE_SHA1} --define=APPLITOOLS_PR_NUMBER=${CIRCLE_PULL_REQUEST##*/} --config=ci -- //android/demo:android_instrumentation_test + - run: + when: always + command: | + RESULTS_DIR=_test_results + find -L ./bazel-testlogs -name test.xml | while read line + do + mkdir -p $RESULTS_DIR/$(dirname $line) + cp $line $RESULTS_DIR/$(dirname $line) + done + + - store_test_results: + path: _test_results + - store_artifacts: path: screenshots diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 494a4e213..000000000 --- a/.editorconfig +++ /dev/null @@ -1,2 +0,0 @@ -[*.{kt,kts}] -disabled_rules=no-wildcard-imports \ No newline at end of file diff --git a/WORKSPACE b/WORKSPACE index b3a243f40..a1b36280b 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -6,14 +6,14 @@ workspace( }, ) -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") http_archive( - name = "rules_player", - strip_prefix = "rules_player-0.10.3", - urls = ["https://github.com/player-ui/rules_player/archive/refs/tags/v0.10.3.tar.gz"], - sha256 = "a21d0fc7428e124bdc5a068ae68e0625a13f8238e685965268b5ed46f4de23fe" + name = "rules_player", + sha256 = "c015a09ce2a5f999a89473cb9f71346c6831e27830a228ebe8f1ba25e83b77b2", + strip_prefix = "rules_player-0.10.4", + urls = ["https://github.com/player-ui/rules_player/archive/refs/tags/v0.10.4.tar.gz"], ) load("@rules_player//:workspace.bzl", "deps") @@ -66,7 +66,7 @@ kotlin() load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_register_toolchains") -kt_register_toolchains() +register_toolchains("//jvm:kotlin_toolchain") load("@rules_player//junit5:conf.bzl", "junit5") @@ -79,33 +79,23 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") grab_remote = "https://github.com/sugarmanz/grab-bazel-common.git" -grab_commit = "a3fe2daf2965b439c8c2a4c2cce1f13beba446b1" +grab_commit = "5326c6ba7a4e39e150c33e123134525473baffb6" git_repository( name = "grab_bazel_common", commit = grab_commit, remote = grab_remote, - shallow_since = "1654123549 -0400", + shallow_since = "1700536974 -0500", ) -# Optional patched Android Tools -load("@grab_bazel_common//:workspace_defs.bzl", "android_tools") +load("@grab_bazel_common//android:repositories.bzl", "bazel_common_dependencies") -android_tools( - commit = grab_commit, - remote = grab_remote, - shallow_since = "1654123549 -0400", -) +bazel_common_dependencies() -DAGGER_TAG = "2.28.1" +load("@grab_bazel_common//android:initialize.bzl", "bazel_common_initialize") -DAGGER_SHA = "9e69ab2f9a47e0f74e71fe49098bea908c528aa02fa0c5995334447b310d0cdd" - -http_archive( - name = "dagger", - sha256 = DAGGER_SHA, - strip_prefix = "dagger-dagger-%s" % DAGGER_TAG, - url = "https://github.com/google/dagger/archive/dagger-%s.zip" % DAGGER_TAG, +bazel_common_initialize( + pinned_maven_install = False, ) http_archive( @@ -119,6 +109,20 @@ load("@robolectric//bazel:robolectric.bzl", "robolectric_repositories") robolectric_repositories() +ANDROIDX_TEST_VERSION = "1.4.2" + +http_file( + name = "android_test_orchestrator_apk", + sha256 = "b7a2e7d0184b03e12c7357f3914d539da40b52a11e90815edff1022c655f459b", + url = "https://dl.google.com/android/maven2/androidx/test/orchestrator/%s/orchestrator-%s.apk" % (ANDROIDX_TEST_VERSION, ANDROIDX_TEST_VERSION), +) + +http_file( + name = "android_test_services_apk", + sha256 = "c6bc74268b29bdabad8da962e00e2f6fd613c24b42c69e81b258397b4819f156", + url = "https://dl.google.com/android/maven2/androidx/test/services/test-services/%s/test-services-%s.apk" % (ANDROIDX_TEST_VERSION, ANDROIDX_TEST_VERSION), +) + http_archive( name = "build_bazel_rules_android", sha256 = "cd06d15dd8bb59926e4d65f9003bfc20f9da4b2519985c27e190cddc8b7a7806", @@ -165,6 +169,7 @@ maven_install( "https://maven.google.com/", "https://plugins.gradle.org/m2/", "https://jcenter.bintray.com/", + "https://jitpack.io/", ], ) @@ -173,6 +178,7 @@ maven_install( artifacts = [ "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:jar:1.5.2", ], + fetch_sources = True, repositories = [ "https://repo1.maven.org/maven2", ], diff --git a/android/build.bzl b/android/build.bzl index fff59f206..2836549f9 100644 --- a/android/build.bzl +++ b/android/build.bzl @@ -6,9 +6,9 @@ def _applitools_config_impl(ctx, **kwargs): head = [ "package %s;" % ctx.attr.package, "public object ApplitoolsConfig {", - " public val API_KEY = " + ("\"" + ctx.var["APPLITOOLS_API_KEY"] + "\"" if "APPLITOOLS_API_KEY" in ctx.var else "\"UNSET\""), - " public val BATCH_ID = " + ("\"" + ctx.var["APPLITOOLS_BATCH_ID"] + "\"" if "APPLITOOLS_BATCH_ID" in ctx.var else "\"local\""), - " public val PR_NUMBER = " + ("\"" + ctx.var["APPLITOOLS_PR_NUMBER"] + "\"" if "APPLITOOLS_PR_NUMBER" in ctx.var else "\"UNSET\""), + " public val API_KEY: String? = " + ("\"" + ctx.var["APPLITOOLS_API_KEY"] + "\"" if "APPLITOOLS_API_KEY" in ctx.var else "null"), + " public val BATCH_ID: String = " + ("\"" + ctx.var["APPLITOOLS_BATCH_ID"] + "\"" if "APPLITOOLS_BATCH_ID" in ctx.var else "\"local\""), + " public val PR_NUMBER: String = " + ("\"" + ctx.var["APPLITOOLS_PR_NUMBER"] + "\"" if "APPLITOOLS_PR_NUMBER" in ctx.var else "\"UNSET\""), ] last = ["}"] values = ctx.attr.values diff --git a/android/demo/BUILD b/android/demo/BUILD index de2c4a4e8..3db069113 100644 --- a/android/demo/BUILD +++ b/android/demo/BUILD @@ -35,6 +35,7 @@ android_binary( kt_jvm_import( name = "kotlinx_coroutines_core_jvm_fixed", jars = ["@kotlinx_coroutines_core_fixed//:v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.2/kotlinx-coroutines-core-jvm-1.5.2.jar"], + srcjar = "@kotlinx_coroutines_core_fixed//:v1/https/repo1.maven.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.5.2/kotlinx-coroutines-core-jvm-1.5.2-sources.jar", visibility = ["//visibility:public"], deps = [ ":sun_dependencies_neverlink", @@ -86,6 +87,8 @@ sh_test( data = [ ":demo", ":demo_test_app", + "@android_test_orchestrator_apk//file", + "@android_test_services_apk//file", ], ) diff --git a/android/demo/deps.bzl b/android/demo/deps.bzl index 9678d1f9f..23c46bd6a 100644 --- a/android/demo/deps.bzl +++ b/android/demo/deps.bzl @@ -5,10 +5,13 @@ maven_main = [ "androidx.navigation:navigation-runtime:%s" % versions.androidx.navigation, "androidx.navigation:navigation-ui-ktx:%s" % versions.androidx.navigation, "androidx.navigation:navigation-fragment-ktx:%s" % versions.androidx.navigation, - + "androidx.lifecycle:lifecycle-viewmodel-ktx:%s" % versions.androidx.lifecycle, + "androidx.lifecycle:lifecycle-runtime-ktx:%s" % versions.androidx.lifecycle, "com.afollestad.material-dialogs:core:%s" % versions.material_dialogs, "com.google.android.material:material:%s" % versions.material, #"com.squareup.leakcanary:leakcanary-android:2.2", + "com.facebook.stetho:stetho:%s" % versions.facebook.stetho, + "com.github.AlexTrotsenko:j2v8-debugger:%s" % versions.j2v8.debugger, ] maven_test = [ diff --git a/android/demo/scripts/androidtest.sh b/android/demo/scripts/androidtest.sh index 34ac50564..24300eba8 100755 --- a/android/demo/scripts/androidtest.sh +++ b/android/demo/scripts/androidtest.sh @@ -2,7 +2,84 @@ set -u -e -o pipefail +# Install app to test adb install android/demo/demo.apk + +# Install test app adb install android/demo/demo_test_app.apk -adb shell am instrument -w com.intuit.player.android.reference.demo.test/androidx.test.runner.AndroidJUnitRunner +DEVICE_API_LEVEL=$(adb shell getprop ro.build.version.sdk) + +FORCE_QUERYABLE_OPTION="" +if [[ $DEVICE_API_LEVEL -ge 30 ]]; then + FORCE_QUERYABLE_OPTION="--force-queryable" +fi + +# TODO: Hooks up properly to runfiles +cp external/android_test_orchestrator_apk/file/downloaded orchestrator.apk +cp external/android_test_services_apk/file/downloaded test_services.apk + +# Install the test orchestrator. +adb install $FORCE_QUERYABLE_OPTION -r orchestrator.apk + +# Install test services. +adb install $FORCE_QUERYABLE_OPTION -r test_services.apk + +adb shell settings put global window_animation_scale 0 +adb shell settings put global transition_animation_scale 0 +adb shell settings put global animator_duration_scale 0 + +# Inspiration: https://gist.github.com/swenson/f797ffea7e243d889406#file-runtests-sh + +# adb shell throws away the return value, so we have to hack do some magic +# see https://code.google.com/p/android/issues/detail?id=3254 + +adb logcat -c && +python - < { @Test fun verifyDefault() { - onView(withText("Android Reference Assets")) + waitForViewInRoot(withText("Android Reference Assets")) .check(matches(isDisplayed())) onView( allOf( - withText("Random Mock") - ) + withText("Random Mock"), + ), ).perform(click()) } } diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/SplashActivityTest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/SplashActivityTest.kt index a005e9932..5f44ffc77 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/SplashActivityTest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/SplashActivityTest.kt @@ -3,12 +3,12 @@ package com.intuit.player.android.reference.demo.test import android.content.Intent import android.net.Uri import androidx.test.core.app.ApplicationProvider -import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.rules.activityScenarioRule import com.intuit.player.android.reference.demo.test.base.ApplitoolsTest +import com.intuit.player.android.reference.demo.test.base.waitForViewInRoot import com.intuit.player.android.reference.demo.ui.splash.SplashActivity import com.intuit.player.jvm.utils.makeFlow import org.junit.Rule @@ -29,7 +29,7 @@ class SplashActivityTest : ApplitoolsTest() { "type": "text", "value": "Deep link test!" } - """.trimIndent() + """.trimIndent(), ) } @@ -45,6 +45,6 @@ class SplashActivityTest : ApplitoolsTest() { @Test fun deepLinkTest() { - onView(withText("Deep link test!")).check(matches(isDisplayed())) + waitForViewInRoot(withText("Deep link test!")).check(matches(isDisplayed())) } } diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/action/ActionUITest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/action/ActionUITest.kt index 7781e38bf..39c021b1b 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/action/ActionUITest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/action/ActionUITest.kt @@ -1,12 +1,12 @@ package com.intuit.player.android.reference.demo.test.assets.action -import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import com.intuit.player.android.reference.demo.test.base.AssetUITest import com.intuit.player.android.reference.demo.test.base.shouldBePlayerState +import com.intuit.player.android.reference.demo.test.base.waitForViewInRoot import com.intuit.player.jvm.core.player.state.CompletedState import com.intuit.player.jvm.core.player.state.ErrorState import com.intuit.player.jvm.core.player.state.InProgressState @@ -14,18 +14,19 @@ import com.intuit.player.jvm.core.player.state.dataModel import org.junit.Assert.assertEquals import org.junit.Test -class ActionUITest : AssetUITest("action") { +class ActionUITest : AssetUITest("reference-assets") { @Test fun basic() { - launchMock() + launchMock("action-basic") repeat(10) { - onView(withText("Count: $it")) - .check(matches(isDisplayed())) + waitForViewInRoot(withText("Count: $it")) .perform(click()) - eyes.checkPlayer("click $it") + waitForViewInRoot(withText("Count: ${it + 1}")) + + eyes?.checkPlayer("click $it") } currentState.shouldBePlayerState { @@ -35,9 +36,9 @@ class ActionUITest : AssetUITest("action") { @Test fun transitionToEndSuccess() { - launchMock("transition-to-end") + launchMock("action-transition-to-end") - onView(withText("End the flow (success)")) + waitForViewInRoot(withText("End the flow (success)")) .check(matches(isDisplayed())) .perform(click()) @@ -48,9 +49,9 @@ class ActionUITest : AssetUITest("action") { @Test fun transitionToEndError() { - launchMock("transition-to-end") + launchMock("action-transition-to-end") - onView(withText("End the flow (error)")) + waitForViewInRoot(withText("End the flow (error)")) .check(matches(isDisplayed())) .perform(click()) diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/collection/CollectionUITest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/collection/CollectionUITest.kt index 86ef2a553..e33df06db 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/collection/CollectionUITest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/collection/CollectionUITest.kt @@ -6,19 +6,23 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import com.intuit.player.android.reference.demo.test.base.AssetUITest import com.intuit.player.android.reference.demo.test.base.shouldBePlayerState +import com.intuit.player.android.reference.demo.test.base.waitForViewInRoot import com.intuit.player.jvm.core.player.state.InProgressState import org.junit.Test -class CollectionUITest : AssetUITest("collection") { +class CollectionUITest : AssetUITest("reference-assets") { @Test fun basic() { - launchMock() + launchMock("collection-basic") - onView(withText("Item 1")) + waitForViewInRoot(withText("Collections are used to group assets.")) .check(matches(isDisplayed())) - onView(withText("Item 2")) + onView(withText("This is the first item in the collection")) + .check(matches(isDisplayed())) + + onView(withText("This is the second item in the collection")) .check(matches(isDisplayed())) currentState.shouldBePlayerState() diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/info/InfoUITest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/info/InfoUITest.kt index ed448ca7f..2c7231d16 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/info/InfoUITest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/info/InfoUITest.kt @@ -7,19 +7,21 @@ import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import com.intuit.player.android.reference.demo.test.base.AssetUITest import com.intuit.player.android.reference.demo.test.base.shouldBePlayerState +import com.intuit.player.android.reference.demo.test.base.waitForViewInRoot import com.intuit.player.jvm.core.player.state.InProgressState import org.junit.Test -class InfoUITest : AssetUITest("info") { +class InfoUITest : AssetUITest("reference-assets") { enum class Action { Next, Dismiss } fun verifyView(view: Int) { - eyes.checkPlayer("View $view") - onView(withText("View $view")) + waitForViewInRoot(withText("View $view")) .check(matches(isDisplayed())) + + eyes?.checkPlayer("View $view") } fun verifyAndProceed(view: Int, action: Action? = null) { @@ -34,7 +36,7 @@ class InfoUITest : AssetUITest("info") { @Test fun basic() { - launchMock("modal-flow") + launchMock("info-modal-flow") verifyAndProceed(1, Action.Next) verifyAndProceed(2, Action.Dismiss) diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/input/InputUITest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/input/InputUITest.kt index 224a74ea8..4a9ef9559 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/input/InputUITest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/input/InputUITest.kt @@ -2,20 +2,18 @@ package com.intuit.player.android.reference.demo.test.assets.input import android.view.View import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.action.ViewActions.clearText import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.pressImeActionButton import androidx.test.espresso.action.ViewActions.typeText import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.hasErrorText import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withChild import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withParent import androidx.test.espresso.matcher.ViewMatchers.withText import com.intuit.player.android.reference.demo.R import com.intuit.player.android.reference.demo.test.base.AssetUITest import com.intuit.player.android.reference.demo.test.base.shouldBePlayerState +import com.intuit.player.android.reference.demo.test.base.waitForViewInRoot import com.intuit.player.jvm.core.player.state.InProgressState import com.intuit.player.jvm.core.player.state.dataModel import org.hamcrest.Matcher @@ -23,16 +21,16 @@ import org.hamcrest.Matchers.allOf import org.junit.Assert.assertEquals import org.junit.Test -class InputUITest : AssetUITest("input") { +class InputUITest : AssetUITest("reference-assets") { - fun verifyIsDisplayed(matcher: Matcher) = onView(matcher) + fun verifyIsDisplayed(matcher: Matcher) = waitForViewInRoot(matcher) .check(matches(isDisplayed())) @Test fun basic() { - launchMock() + launchMock("input-basic") - onView(withText("This is an input")) + waitForViewInRoot(withText("This is an input")) .check(matches(isDisplayed())) onView(withId(R.id.input_field)) @@ -47,81 +45,39 @@ class InputUITest : AssetUITest("input") { } @Test - fun validationAgeFormatter() { - launchMock("validation") + fun validation() { + launchMock("input-validation") + + verifyIsDisplayed( + allOf( + withId(R.id.input_label_container), + withChild(withText("Input with validation and formatting")), + ), + ) + + verifyIsDisplayed( + allOf( + withId(R.id.input_note_container), + withChild(withText("It expects a positive integer")), + ), + ) - val ageContainer = allOf( - withId(R.id.input_container), - withChild(withChild(withText("Age"))) - ).also(::verifyIsDisplayed) - - val ageInput = allOf( - withId(R.id.input_field), - withParent(ageContainer) - ).also(::verifyIsDisplayed) - - onView(ageInput) - .perform(typeText("text")) + onView(withId(R.id.input_field)) + .perform(typeText("t")) .perform(pressImeActionButton()) currentState.shouldBePlayerState { - assertEquals("", dataModel.get("person.age")) + assertEquals(null, dataModel.get("foo.bar")) } - eyes.checkPlayer("invalid-age") + eyes?.checkPlayer("invalid-input") - onView(ageInput) + onView(withId(R.id.input_field)) .perform(typeText("30")) .perform(pressImeActionButton()) currentState.shouldBePlayerState { - assertEquals(30, dataModel.get("person.age")) - } - } - - @Test - fun validationName() { - launchMock("validation") - - val nameContainer = allOf( - withId(R.id.input_container), - withChild(withChild(withText("Name"))) - ).also(::verifyIsDisplayed) - - val nameInput = allOf( - withId(R.id.input_field), - withParent(nameContainer) - ).also(::verifyIsDisplayed) - - onView(nameInput) - .perform(typeText("more than 10 characters")) - .perform(pressImeActionButton()) - .check(matches(hasErrorText("Up to 10 characters allowed"))) - - currentState.shouldBePlayerState { - assertEquals("", dataModel.get("person.name")) - } - - eyes.checkPlayer("upper-limit-validation") - - onView(nameInput) - .perform(click()) - .perform(clearText()) - .perform(pressImeActionButton()) - .check(matches(hasErrorText("At least 1 characters needed"))) - - currentState.shouldBePlayerState { - assertEquals("", dataModel.get("person.name")) - } - - eyes.checkPlayer("lower-limit-validation") - - onView(nameInput) - .perform(typeText("Jeremiah")) - .perform(pressImeActionButton()) - - currentState.shouldBePlayerState { - assertEquals("Jeremiah", dataModel.get("person.name")) + assertEquals(30, dataModel.get("foo.bar")) } } } diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/text/TextUITest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/text/TextUITest.kt index 078edfcbf..e15db59bc 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/text/TextUITest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/assets/text/TextUITest.kt @@ -3,7 +3,6 @@ package com.intuit.player.android.reference.demo.test.assets.text import android.app.Activity.RESULT_CANCELED import android.app.Instrumentation.ActivityResult import android.content.Intent.ACTION_VIEW -import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents.intended @@ -13,31 +12,32 @@ import androidx.test.espresso.intent.matcher.IntentMatchers.hasData import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withText import com.intuit.player.android.reference.demo.test.base.AssetUITest +import com.intuit.player.android.reference.demo.test.base.waitForViewInRoot import org.hamcrest.Matchers.allOf import org.junit.Test -class TextUITest : AssetUITest("text") { +class TextUITest : AssetUITest("reference-assets") { @Test fun basic() { - launchMock() + launchMock("text-basic") - onView(withText("Some text content")) + waitForViewInRoot(withText("This is some text.")) .check(matches(isDisplayed())) } @Test fun link() { - launchMock("with-link") + launchMock("text-with-link") val openLink = allOf( hasAction(ACTION_VIEW), - hasData("http://www.intuit.com") + hasData("http://www.intuit.com"), ) intending(openLink).respondWith(ActivityResult(RESULT_CANCELED, null)) - onView(withText("A Link")) + waitForViewInRoot(withText("A Link")) .check(matches(isDisplayed())) .perform(click()) diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/ApplitoolsTest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/ApplitoolsTest.kt index c0d44e796..b86bac4d2 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/ApplitoolsTest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/ApplitoolsTest.kt @@ -21,16 +21,18 @@ abstract class ApplitoolsTest { } // Initialize the eyes SDK and set your private API key. - val eyes by lazy { - Eyes().apply { - componentsProvider = AndroidXComponentsProvider() - configuration = Configuration().apply { - appName = "Android Reference Assets" - addProperty("platform", "android") - batch = batchInfo - apiKey = API_KEY - setFeatures(Feature.PIXEL_COPY_SCREENSHOT) - setServerUrl("https://intuiteyesapi.applitools.com") + val eyes: Eyes? by lazy { + API_KEY?.let { + Eyes().apply { + componentsProvider = AndroidXComponentsProvider() + configuration = Configuration().apply { + appName = "Android Reference Assets" + addProperty("platform", "android") + batch = batchInfo + apiKey = API_KEY + setFeatures(Feature.PIXEL_COPY_SCREENSHOT) + setServerUrl("https://intuiteyesapi.applitools.com") + } } } } diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/assertions.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/Assertions.kt similarity index 97% rename from android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/assertions.kt rename to android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/Assertions.kt index 17fc1a31f..bbaf7beb4 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/assertions.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/Assertions.kt @@ -9,7 +9,7 @@ import kotlin.contracts.contract @OptIn(ExperimentalContracts::class) inline fun Any?.shouldBeAsset( - block: T.() -> Unit = {} + block: T.() -> Unit = {}, ): T { shouldBeInstanceOf(this) block() diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/AssetUITest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/AssetUITest.kt index 1b02951f6..e4f74a977 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/AssetUITest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/AssetUITest.kt @@ -40,10 +40,11 @@ abstract class AssetUITest(val group: String? = null) : ApplitoolsTest() { @After fun after() { - Intents.assertNoUnverifiedIntents() + eyes?.takeIf { it.isOpen }?.run { + checkWindow("done") + close() + } Intents.release() - eyes.checkWindow("done") - eyes.close() } fun launchMock() { @@ -53,7 +54,7 @@ abstract class AssetUITest(val group: String? = null) : ApplitoolsTest() { fun launchMock(name: String) { launchMock( mocks.find { it.name == name || it.name == "$group-$name" } - ?: throw IllegalArgumentException("$name not found in mocks: ${mocks.map { "${it.group}/${it.name}" }}") + ?: throw IllegalArgumentException("$name not found in mocks: ${mocks.map { "${it.group}/${it.name}" }}"), ) } @@ -65,7 +66,7 @@ abstract class AssetUITest(val group: String? = null) : ApplitoolsTest() { ?: throw IllegalStateException("player not found") } - eyes.open("Android Reference Assets Demo", "${mock.group}/${name.methodName}") - eyes.checkPlayer("init") + eyes?.open("Android Reference Assets Demo", "${mock.group}/${mock.name}/${name.methodName}") + eyes?.checkPlayer("init") } } diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/Matchers.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/Matchers.kt new file mode 100644 index 000000000..30c667489 --- /dev/null +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/Matchers.kt @@ -0,0 +1,58 @@ +package com.intuit.player.android.reference.demo.test.base + +import android.view.View +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.PerformException +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import androidx.test.espresso.ViewInteraction +import androidx.test.espresso.matcher.ViewMatchers +import androidx.test.espresso.matcher.ViewMatchers.isRoot +import androidx.test.espresso.util.HumanReadables +import androidx.test.espresso.util.TreeIterables +import org.hamcrest.Matcher +import org.hamcrest.Matchers +import org.hamcrest.StringDescription +import java.util.concurrent.TimeoutException + +fun waitForViewInRoot(viewMatcher: Matcher, timeout: Long = 10000, waitForDisplayed: Boolean = true): ViewInteraction { + onView(isRoot()).perform(waitForView(viewMatcher, timeout, waitForDisplayed)) + + return onView(viewMatcher) +} + +fun waitForView(viewMatcher: Matcher, timeout: Long = 10000, waitForDisplayed: Boolean = true): ViewAction { + return object : ViewAction { + override fun getConstraints(): Matcher { + return Matchers.any(View::class.java) + } + + override fun getDescription(): String { + val matcherDescription = StringDescription() + viewMatcher.describeTo(matcherDescription) + return "wait for a specific view <$matcherDescription> to be ${if (waitForDisplayed) "displayed" else "not displayed during $timeout millis."}" + } + + override fun perform(uiController: UiController, view: View) { + uiController.loopMainThreadUntilIdle() + val startTime = System.currentTimeMillis() + val endTime = startTime + timeout + val visibleMatcher = ViewMatchers.isDisplayed() + + do { + val viewVisible = TreeIterables.breadthFirstViewTraversal(view) + .any { viewMatcher.matches(it) && visibleMatcher.matches(it) } + + if (viewVisible == waitForDisplayed) return + uiController.loopMainThreadForAtLeast(50) + } while (System.currentTimeMillis() < endTime) + + // Timeout happens. + throw PerformException.Builder() + .withActionDescription(this.description) + .withViewDescription(HumanReadables.describe(view)) + .withCause(TimeoutException()) + .build() + } + } +} diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/PerformanceTest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/PerformanceTest.kt index 94b2479fa..40be25c3d 100644 --- a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/PerformanceTest.kt +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/base/PerformanceTest.kt @@ -36,20 +36,20 @@ interface PerformanceTest { val averageTime = calculateAverage(histogram, totalFrames) assertTrue( "Over a total of $totalFrames frames, the slowest, median and average time it took for frames to render was ${maxTime}ms, ${medianTime}ms, and ${averageTime}ms respectively", - (maxTime < 250 && medianTime < 32 && averageTime < 24) || true + (maxTime < 250 && medianTime < 32 && averageTime < 24) || true, ) activityRule.scenario.close() } enum class FrameStats constructor( - val pattern: Pattern + val pattern: Pattern, ) { TOTAL_FRAMES(Pattern.compile("\\s*$totalFrames: (\\d+)")), FIFTY_PERCENTILE(Pattern.compile("\\s*$fiftiethPercentile: (\\d+)ms")), NINETY_PERCENTILE(Pattern.compile("\\s*$ninetiethPercentile: (\\d+)ms")), NINETY_FIVE_PERCENTILE(Pattern.compile("\\s*$ninetyFifthPercentile: (\\d+)ms")), NINETY_NINE_PERCENTILE(Pattern.compile("\\s*$ninetyNinethPercentile: (\\d+)ms")), - HISTOGRAM(Pattern.compile("\\s*HISTOGRAM: (.*)")) + HISTOGRAM(Pattern.compile("\\s*HISTOGRAM: (.*)")), } fun startPerformanceTest() { diff --git a/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/fragment/PlayerFragmentScrollingTest.kt b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/fragment/PlayerFragmentScrollingTest.kt new file mode 100644 index 000000000..a6f950108 --- /dev/null +++ b/android/demo/src/androidTest/java/com/intuit/player/android/reference/demo/test/fragment/PlayerFragmentScrollingTest.kt @@ -0,0 +1,20 @@ +package com.intuit.player.android.reference.demo.test.fragment + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withText +import com.intuit.player.android.reference.demo.test.base.AssetUITest +import org.junit.Test + +class PlayerFragmentScrollingTest : AssetUITest("misc") { + + @Test + fun shouldScrollToTopOnTransition() { + launchMock("long-multi-view") + onView(withText("Go to view 2")).perform(scrollTo(), click()) + onView(withText("Can you see me?")).check(matches(isDisplayed())) + } +} diff --git a/android/demo/src/main/assets/mocks/misc/long-multi-view.json b/android/demo/src/main/assets/mocks/misc/long-multi-view.json new file mode 100644 index 000000000..2680fef2f --- /dev/null +++ b/android/demo/src/main/assets/mocks/misc/long-multi-view.json @@ -0,0 +1,73 @@ +{ + "id": "long-multi-view", + "views": [ + { + "id": "view-1", + "type": "collection", + "values": [ + { + "asset": { + "id": "view-1-text", + "type": "text", + "value": "It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way—in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only.\n\nThere were a king with a large jaw and a queen with a plain face, on the throne of England; there were a king with a large jaw and a queen with a fair face, on the throne of France. In both countries it was clearer than crystal to the lords of the State preserves of loaves and fishes, that things in general were settled for ever.\n\nIt was the year of Our Lord one thousand seven hundred and seventy-five. Spiritual revelations were conceded to England at that favoured period, as at this. Mrs. Southcott had recently attained her five-and-twentieth blessed birthday, of whom a prophetic private in the Life Guards had heralded the sublime appearance by announcing that arrangements were made for the swallowing up of London and Westminster. Even the Cock-lane ghost had been laid only a round dozen of years, after rapping out its messages, as the spirits of this very year last past (supernaturally deficient in originality) rapped out theirs. Mere messages in the earthly order of events had lately come to the English Crown and People, from a congress of British subjects in America: which, strange to relate, have proved more important to the human race than any communications yet received through any of the chickens of the Cock-lane brood.\n\nFrance, less favoured on the whole as to matters spiritual than her sister of the shield and trident, rolled with exceeding smoothness down hill, making paper money and spending it. Under the guidance of her Christian pastors, she entertained herself, besides, with such humane achievements as sentencing a youth to have his hands cut off, his tongue torn out with pincers, and his body burned alive, because he had not kneeled down in the rain to do honour to a dirty procession of monks which passed within his view, at a distance of some fifty or sixty yards. It is likely enough that, rooted in the woods of France and Norway, there were growing trees, when that sufferer was put to death, already marked by the Woodman, Fate, to come down and be sawn into boards, to make a certain movable framework with a sack and a knife in it, terrible in history. It is likely enough that in the rough outhouses of some tillers of the heavy lands adjacent to Paris, there were sheltered from the weather that very day, rude carts, bespattered with rustic mire, snuffed about by pigs, and roosted in by poultry, which the Farmer, Death, had already set apart to be his tumbrils of the Revolution. But that Woodman and that Farmer, though they work unceasingly, work silently, and no one heard them as they went about with muffled tread: the rather, forasmuch as to entertain any suspicion that they were awake, was to be atheistical and traitorous.\n\nIn England, there was scarcely an amount of order and protection to justify much national boasting. Daring burglaries by armed men, and highway robberies, took place in the capital itself every night; families were publicly cautioned not to go out of town without removing their furniture to upholsterers’ warehouses for security; the highwayman in the dark was a City tradesman in the light, and, being recognised and challenged by his fellow-tradesman whom he stopped in his character of “the Captain,” gallantly shot him through the head and rode away; the mail was waylaid by seven robbers, and the guard shot three dead, and then got shot dead himself by the other four, “in consequence of the failure of his ammunition:” after which the mail was robbed in peace; that magnificent potentate, the Lord Mayor of London, was made to stand and deliver on Turnham Green, by one highwayman, who despoiled the illustrious creature in sight of all his retinue; prisoners in London gaols fought battles with their turnkeys, and the majesty of the law fired blunderbusses in among them, loaded with rounds of shot and ball; thieves snipped off diamond crosses from the necks of noble lords at Court drawing-rooms; musketeers went into St. Giles’s, to search for contraband goods, and the mob fired on the musketeers, and the musketeers fired on the mob, and nobody thought any of these occurrences much out of the common way. In the midst of them, the hangman, ever busy and ever worse than useless, was in constant requisition; now, stringing up long rows of miscellaneous criminals; now, hanging a housebreaker on Saturday who had been taken on Tuesday; now, burning people in the hand at Newgate by the dozen, and now burning pamphlets at the door of Westminster Hall; to-day, taking the life of an atrocious murderer, and to-morrow of a wretched pilferer who had robbed a farmer’s boy of sixpence.\n\nAll these things, and a thousand like them, came to pass in and close upon the dear old year one thousand seven hundred and seventy-five. Environed by them, while the Woodman and the Farmer worked unheeded, those two of the large jaws, and those other two of the plain and the fair faces, trod with stir enough, and carried their divine rights with a high hand. Thus did the year one thousand seven hundred and seventy-five conduct their Greatnesses, and myriads of small creatures—the creatures of this chronicle among the rest—along the roads that lay before them." + } + }, + { + "asset": { + "id": "view-1-action", + "type": "action", + "label": { + "asset": { + "id": "view-1-action-label", + "type": "text", + "value": "Go to view 2" + } + }, + "value": "GoToView2" + } + } + ] + }, + { + "id": "view-2", + "type": "collection", + "values": [ + { + "asset": { + "id": "view-2-text-1", + "type": "text", + "value": "Can you see me?" + } + }, + { + "asset": { + "id": "view-2-text-2", + "type": "text", + "value": "The Time Traveller (for so it will be convenient to speak of him) was expounding a recondite matter to us. His pale grey eyes shone and twinkled, and his usually pale face was flushed and animated. The fire burnt brightly, and the soft radiance of the incandescent lights in the lilies of silver caught the bubbles that flashed and passed in our glasses. Our chairs, being his patents, embraced and caressed us rather than submitted to be sat upon, and there was that luxurious after-dinner atmosphere, when thought runs gracefully free of the trammels of precision. And he put it to us in this way—marking the points with a lean forefinger—as we sat and lazily admired his earnestness over this new paradox (as we thought it) and his fecundity.\n\n“You must follow me carefully. I shall have to controvert one or two ideas that are almost universally accepted. The geometry, for instance, they taught you at school is founded on a misconception.”\n\n“Is not that rather a large thing to expect us to begin upon?” said Filby, an argumentative person with red hair.\n\n“I do not mean to ask you to accept anything without reasonable ground for it. You will soon admit as much as I need from you. You know of course that a mathematical line, a line of thickness nil, has no real existence. They taught you that? Neither has a mathematical plane. These things are mere abstractions.”\n\n“That is all right,” said the Psychologist.\n\n“Nor, having only length, breadth, and thickness, can a cube have a real existence.”\n\n“There I object,” said Filby. “Of course a solid body may exist. All real things—”\n\n“So most people think. But wait a moment. Can an instantaneous cube exist?”\n\n“Don’t follow you,” said Filby.\n\n“Can a cube that does not last for any time at all, have a real existence?”\n\nFilby became pensive. “Clearly,” the Time Traveller proceeded, “any real body must have extension in four directions: it must have Length, Breadth, Thickness, and—Duration. But through a natural infirmity of the flesh, which I will explain to you in a moment, we incline to overlook this fact. There are really four dimensions, three which we call the three planes of Space, and a fourth, Time. There is, however, a tendency to draw an unreal distinction between the former three dimensions and the latter, because it happens that our consciousness moves intermittently in one direction along the latter from the beginning to the end of our lives.”\n\n“That,” said a very young man, making spasmodic efforts to relight his cigar over the lamp; “that . . . very clear indeed.”\n\n“Now, it is very remarkable that this is so extensively overlooked,” continued the Time Traveller, with a slight accession of cheerfulness. “Really this is what is meant by the Fourth Dimension, though some people who talk about the Fourth Dimension do not know they mean it. It is only another way of looking at Time. There is no difference between Time and any of the three dimensions of Space except that our consciousness moves along it. But some foolish people have got hold of the wrong side of that idea. You have all heard what they have to say about this Fourth Dimension?”\n\n“I have not,” said the Provincial Mayor.\n\n“It is simply this. That Space, as our mathematicians have it, is spoken of as having three dimensions, which one may call Length, Breadth, and Thickness, and is always definable by reference to three planes, each at right angles to the others. But some philosophical people have been asking why three dimensions particularly—why not another direction at right angles to the other three?—and have even tried to construct a Four-Dimensional geometry. Professor Simon Newcomb was expounding this to the New York Mathematical Society only a month or so ago. You know how on a flat surface, which has only two dimensions, we can represent a figure of a three-dimensional solid, and similarly they think that by models of three dimensions they could represent one of four—if they could master the perspective of the thing. See?”\n\n“I think so,” murmured the Provincial Mayor; and, knitting his brows, he lapsed into an introspective state, his lips moving as one who repeats mystic words. “Yes, I think I see it now,” he said after some time, brightening in a quite transitory manner.\n\n“Well, I do not mind telling you I have been at work upon this geometry of Four Dimensions for some time. Some of my results are curious. For instance, here is a portrait of a man at eight years old, another at fifteen, another at seventeen, another at twenty-three, and so on. All these are evidently sections, as it were, Three-Dimensional representations of his Four-Dimensioned being, which is a fixed and unalterable thing.\n\n“Scientific people,” proceeded the Time Traveller, after the pause required for the proper assimilation of this, “know very well that Time is only a kind of Space. Here is a popular scientific diagram, a weather record. This line I trace with my finger shows the movement of the barometer. Yesterday it was so high, yesterday night it fell, then this morning it rose again, and so gently upward to here. Surely the mercury did not trace this line in any of the dimensions of Space generally recognised? But certainly it traced such a line, and that line, therefore, we must conclude, was along the Time-Dimension.”\n\n“But,” said the Medical Man, staring hard at a coal in the fire, “if Time is really only a fourth dimension of Space, why is it, and why has it always been, regarded as something different? And why cannot we move in Time as we move about in the other dimensions of Space?”\n\nThe Time Traveller smiled. “Are you so sure we can move freely in Space? Right and left we can go, backward and forward freely enough, and men always have done so. I admit we move freely in two dimensions. But how about up and down? Gravitation limits us there.”\n\n“Not exactly,” said the Medical Man. “There are balloons.”\n\n“But before the balloons, save for spasmodic jumping and the inequalities of the surface, man had no freedom of vertical movement.”\n\n“Still they could move a little up and down,” said the Medical Man.\n\n“Easier, far easier down than up.”\n\n“And you cannot move at all in Time, you cannot get away from the present moment.”\n\n“My dear sir, that is just where you are wrong. That is just where the whole world has gone wrong. We are always getting away from the present moment. Our mental existences, which are immaterial and have no dimensions, are passing along the Time-Dimension with a uniform velocity from the cradle to the grave. Just as we should travel down if we began our existence fifty miles above the earth’s surface.”" + } + } + ] + } + ], + "navigation": { + "BEGIN": "Flow", + "Flow": { + "startState": "VIEW_1", + "VIEW_1": { + "state_type": "VIEW", + "ref": "view-1", + "transitions": { + "GoToView2": "VIEW_2", + "*": "END_done" + } + }, + "VIEW_2": { + "state_type": "VIEW", + "ref": "view-2", + "transitions": { + "*": "END_done" + } + } + } + } +} \ No newline at end of file diff --git a/android/demo/src/main/java/com/intuit/player/android/reference/demo/lifecycle/DemoPlayerViewModel.kt b/android/demo/src/main/java/com/intuit/player/android/reference/demo/lifecycle/DemoPlayerViewModel.kt index 40eeec536..8bb74b997 100644 --- a/android/demo/src/main/java/com/intuit/player/android/reference/demo/lifecycle/DemoPlayerViewModel.kt +++ b/android/demo/src/main/java/com/intuit/player/android/reference/demo/lifecycle/DemoPlayerViewModel.kt @@ -1,6 +1,7 @@ package com.intuit.player.android.reference.demo.lifecycle import com.intuit.player.android.AndroidPlayer +import com.intuit.player.android.AndroidPlayer.Config import com.intuit.player.android.lifecycle.PlayerViewModel import com.intuit.player.android.reference.assets.ReferenceAssetsPlugin import com.intuit.player.jvm.core.managed.AsyncFlowIterator @@ -19,6 +20,10 @@ class DemoPlayerViewModel(iterator: AsyncFlowIterator) : PlayerViewModel(iterato PendingTransactionPlugin(), ) + override val config: Config = Config( + debuggable = true, + ) + private val _playerFlowState = MutableStateFlow(null) public val playerFlowState: StateFlow = _playerFlowState.asStateFlow() diff --git a/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/AssetMock.kt b/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/AssetMock.kt index 7efa13733..b56093c1c 100644 --- a/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/AssetMock.kt +++ b/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/AssetMock.kt @@ -6,7 +6,7 @@ import com.intuit.player.jvm.utils.mocks.Mock open class AssetMock( override val group: String, override val name: String, - override val path: String + override val path: String, ) : Mock { override fun read(source: AssetManager) = source .open(normalizedPath) diff --git a/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/StringMock.kt b/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/StringMock.kt index 12dfb2dd5..ce5cf5fea 100644 --- a/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/StringMock.kt +++ b/android/demo/src/main/java/com/intuit/player/android/reference/demo/model/StringMock.kt @@ -6,7 +6,7 @@ class StringMock( private val json: String, override val group: String = "", override val name: String = "Player", - override val path: String = "" + override val path: String = "", ) : Mock { override fun read(source: Any) = json } diff --git a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/base/BasePlayerFragment.kt b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/base/BasePlayerFragment.kt index 3e938f52f..3030ab89a 100644 --- a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/base/BasePlayerFragment.kt +++ b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/base/BasePlayerFragment.kt @@ -1,17 +1,25 @@ package com.intuit.player.android.reference.demo.ui.base import android.graphics.drawable.GradientDrawable +import android.os.Bundle +import android.view.LayoutInflater import android.view.View -import androidx.core.view.get +import android.view.ViewGroup import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.afollestad.materialdialogs.MaterialDialog +import com.alexii.j2v8debugger.StethoHelper import com.intuit.player.android.lifecycle.ManagedPlayerState import com.intuit.player.android.lifecycle.PlayerViewModel import com.intuit.player.android.reference.demo.lifecycle.DemoPlayerViewModel import com.intuit.player.android.ui.PlayerFragment +import com.intuit.player.jvm.core.bridge.serialization.json.prettify +import com.intuit.player.jvm.core.bridge.toJson import com.intuit.player.jvm.core.managed.AsyncFlowIterator -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** Simple [PlayerFragment] example that builds a [DemoPlayerViewModel] w/ a single flow iterator */ abstract class BasePlayerFragment : PlayerFragment() { @@ -24,10 +32,19 @@ abstract class BasePlayerFragment : PlayerFragment() { private val currentPlayerCanvas get() = binding.playerCanvas.getChildAt(0) + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + StethoHelper.initializeDebugger(requireContext(), playerViewModel.player) + return super.onCreateView(inflater, container, savedInstanceState) + } + private suspend fun toggleScreenShare(active: Boolean) = withContext(Dispatchers.Main) { - binding.playerCanvas.background = if (active) GradientDrawable().apply { - setStroke(30, resources.getColor(android.R.color.holo_green_light)) - } else null + binding.playerCanvas.background = if (active) { + GradientDrawable().apply { + setStroke(30, resources.getColor(android.R.color.holo_green_light)) + } + } else { + null + } } override fun buildFallbackView(exception: Exception): View? = currentPlayerCanvas @@ -35,27 +52,31 @@ abstract class BasePlayerFragment : PlayerFragment() { override fun buildDoneView(): View? = currentPlayerCanvas override fun onDone(doneState: ManagedPlayerState.Done) { + val message = doneState.completedState?.endState?.node?.toJson()?.prettify() showDialog { title(text = "Flows completed successfully!") - message(text = doneState.completedState?.endState.toString()) + message(text = message) } } override fun onError(errorState: ManagedPlayerState.Error) { + val message = errorState.exception.message showDialog { title(text = "Error in Flow!") - message(text = errorState.exception.message) + message(text = message) } } protected fun showDialog(builder: MaterialDialog.() -> Unit) { - MaterialDialog(requireContext()).show { - positiveButton(text = "Reset") { reset() } - negativeButton(text = "Back") { - findNavController().popBackStack() + lifecycleScope.launch(Dispatchers.Main) { + MaterialDialog(requireContext()).show { + positiveButton(text = "Reset") { reset() } + negativeButton(text = "Back") { + findNavController().popBackStack() + } + cancelable(false) + builder() } - cancelable(false) - builder() } } } diff --git a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainActivity.kt b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainActivity.kt index e290995ce..565a46a0f 100644 --- a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainActivity.kt +++ b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainActivity.kt @@ -7,11 +7,18 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.os.bundleOf import androidx.core.view.GravityCompat import androidx.drawerlayout.widget.DrawerLayout +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.observe +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment -import androidx.navigation.ui.* +import androidx.navigation.fragment.NavHostFragment.findNavController +import androidx.navigation.ui.AppBarConfiguration +import androidx.navigation.ui.navigateUp +import androidx.navigation.ui.onNavDestinationSelected +import androidx.navigation.ui.setupActionBarWithNavController +import androidx.navigation.ui.setupWithNavController import com.google.android.material.navigation.NavigationView import com.intuit.player.android.reference.demo.R import com.intuit.player.android.reference.demo.model.AssetMock @@ -20,6 +27,8 @@ import com.intuit.player.android.ui.PlayerFragment import com.intuit.player.jvm.utils.mocks.ClassLoaderMock import com.intuit.player.jvm.utils.mocks.Mock import com.intuit.player.jvm.utils.mocks.getFlow +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { @@ -36,6 +45,16 @@ class MainActivity : AppCompatActivity() { return navController.navigateUp(navConfig) || super.onSupportNavigateUp() } + init { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + viewModel.currentMock.collect { + startFlow(it) + } + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -48,9 +67,6 @@ class MainActivity : AppCompatActivity() { setupActionBarWithNavController(navController, navConfig) storybookNav.setupWithNavController(navController) storybookNav.menu.let(viewModel::groupMocks) - viewModel.currentMock.observe(this) { - startFlow(it) - } } override fun onCreateOptionsMenu(menu: Menu?): Boolean { @@ -84,7 +100,7 @@ class MainActivity : AppCompatActivity() { is StringMock -> mock.getFlow("") else -> throw IllegalArgumentException("mock of type ${mock::class}[$mock] not supported") }, - mock.name + mock.name, ) private fun launchFlow(flow: String, name: String?) { @@ -93,7 +109,7 @@ class MainActivity : AppCompatActivity() { R.id.action_launch_player, name?.let { bundleOf("name" to name, "flow" to flow) - } ?: bundleOf("flow" to flow) + } ?: bundleOf("flow" to flow), ) } } diff --git a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainViewModel.kt b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainViewModel.kt index 7ea347116..540239c97 100644 --- a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainViewModel.kt +++ b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/main/MainViewModel.kt @@ -3,12 +3,15 @@ package com.intuit.player.android.reference.demo.ui.main import android.app.Application import android.view.Menu import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.viewModelScope import com.intuit.player.android.reference.demo.model.AssetMock import com.intuit.player.android.reference.demo.model.StringMock import com.intuit.player.jvm.utils.mocks.ClassLoaderMocksReader import com.intuit.player.jvm.utils.mocks.Mock +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch import java.io.FileNotFoundException class MainViewModel(private val context: Application) : AndroidViewModel(context) { @@ -21,14 +24,16 @@ class MainViewModel(private val context: Application) : AndroidViewModel(context AssetMock("", "default", "mocks/default.json") } - private val _currentMock = MutableLiveData>() + private val _currentMock = MutableSharedFlow>() /** [currentMock] LiveData that represents the mock that is currently displayed */ - val currentMock: LiveData> get() = _currentMock + val currentMock: SharedFlow> = _currentMock.asSharedFlow() fun launch(json: String) = launch(StringMock(json)) - fun launch(mock: Mock<*>) = _currentMock.postValue(mock) + fun launch(mock: Mock<*>) = viewModelScope.launch { + _currentMock.emit(mock) + } private fun readMocksFromClasspath() = ClassLoaderMocksReader(MainViewModel::class.java.classLoader!!).mocks diff --git a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/start/StartFragment.kt b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/start/StartFragment.kt index 28d3dcdb6..6e7a76d40 100644 --- a/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/start/StartFragment.kt +++ b/android/demo/src/main/java/com/intuit/player/android/reference/demo/ui/start/StartFragment.kt @@ -4,10 +4,13 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup import androidx.fragment.app.activityViewModels +import androidx.lifecycle.lifecycleScope import com.intuit.player.android.lifecycle.ManagedPlayerState import com.intuit.player.android.reference.demo.ui.base.BasePlayerFragment import com.intuit.player.android.reference.demo.ui.main.MainViewModel import com.intuit.player.jvm.utils.mocks.getFlow +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class StartFragment : BasePlayerFragment() { private val mainViewModel: MainViewModel by activityViewModels() @@ -19,9 +22,21 @@ class StartFragment : BasePlayerFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ) = super.onCreateView(inflater, container, savedInstanceState).apply { - reset() + when (val state = playerViewModel.state.value) { + is ManagedPlayerState.Running -> lifecycleScope.launch(Dispatchers.Default) { + handleAssetUpdate(state.asset, state.animateViewTransition) + } + + is ManagedPlayerState.Done, + is ManagedPlayerState.Error, + -> reset() + + ManagedPlayerState.NotStarted, + ManagedPlayerState.Pending, + -> Unit + } } override fun onDone(doneState: ManagedPlayerState.Done) { diff --git a/android/player/BUILD b/android/player/BUILD index dab9b7a25..f0ec0c75e 100644 --- a/android/player/BUILD +++ b/android/player/BUILD @@ -11,9 +11,9 @@ kt_db_android_library( manifest = ":src/main/AndroidManifest.xml", resource_files = glob(["src/main/res/**"]), resources = main_resources, - deps = main_deps, + tags = ["maven_coordinates=com.intuit.player.android:player:{pom_version}"], visibility = ["//visibility:public"], - tags = ["maven_coordinates=com.intuit.player.android:player:{pom_version}"] + deps = main_deps, ) distribution( diff --git a/android/player/api/player.api b/android/player/api/player.api index ec64dc481..86aa4e0d5 100644 --- a/android/player/api/player.api +++ b/android/player/api/player.api @@ -233,10 +233,10 @@ public class com/intuit/player/android/lifecycle/PlayerViewModel : androidx/life public final fun fail (Ljava/lang/Throwable;)V public final fun getBeacons ()Lkotlinx/coroutines/flow/SharedFlow; protected final fun getManager ()Lcom/intuit/player/jvm/core/managed/AsyncIterationManager; - protected final fun getPlayer ()Lcom/intuit/player/android/AndroidPlayer; + public final fun getPlayer ()Lcom/intuit/player/android/AndroidPlayer; protected fun getPlugins ()Ljava/util/List; public final fun getState ()Lkotlinx/coroutines/flow/StateFlow; - protected fun onCleared ()V + public fun onCleared ()V public final fun recycle ()V public final fun release ()V public final fun retry ()V diff --git a/android/player/deps.bzl b/android/player/deps.bzl index 0d340505a..b6aa15d2a 100644 --- a/android/player/deps.bzl +++ b/android/player/deps.bzl @@ -16,7 +16,7 @@ maven = [ ] main_exports = [ - "//jvm/j2v8:j2v8-android" + "//jvm/j2v8:j2v8-android", ] main_deps = main_exports + parse_coordinates(maven) + [ @@ -36,4 +36,5 @@ test_deps = [ "@grab_bazel_common//tools/test:mockable-android-jar", "@maven//:io_mockk_mockk", "//jvm/testutils", + "@maven//:org_jetbrains_kotlinx_kotlinx_coroutines_test", ] diff --git a/android/player/src/androidTest/java/com/intuit/player/android/AssetContextAndroidTest.kt b/android/player/src/androidTest/java/com/intuit/player/android/AssetContextAndroidTest.kt index eb7995a22..091895357 100644 --- a/android/player/src/androidTest/java/com/intuit/player/android/AssetContextAndroidTest.kt +++ b/android/player/src/androidTest/java/com/intuit/player/android/AssetContextAndroidTest.kt @@ -22,6 +22,7 @@ internal class AssetContextAndroidTest { private fun Map.toAsset(): Asset = Node(this).let(::Asset) private val assetMap = mapOf("id" to "first", "type" to "rando") + @Suppress("DEPRECATION_ERROR") private fun renderableAsset(assetContext: AssetContext) = object : RenderableAsset(assetContext) { override fun initView() = TextView(context) diff --git a/android/player/src/main/java/com/intuit/player/android/AndroidPlayer.kt b/android/player/src/main/java/com/intuit/player/android/AndroidPlayer.kt index 918ae56cd..2745aeffc 100644 --- a/android/player/src/main/java/com/intuit/player/android/AndroidPlayer.kt +++ b/android/player/src/main/java/com/intuit/player/android/AndroidPlayer.kt @@ -2,9 +2,15 @@ package com.intuit.player.android import android.content.Context import android.view.View +import com.alexii.j2v8debugger.ScriptSourceProvider +import com.intuit.hooks.BailResult import com.intuit.hooks.HookContext +import com.intuit.hooks.SyncBailHook +import com.intuit.hooks.SyncHook import com.intuit.hooks.SyncWaterfallHook +import com.intuit.player.android.AndroidPlayer.Companion.injectDefaultPlugins import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.debug.UnsupportedScriptProvider import com.intuit.player.android.extensions.Styles import com.intuit.player.android.extensions.overlayStyles import com.intuit.player.android.extensions.removeSelf @@ -13,22 +19,27 @@ import com.intuit.player.android.registry.RegistryPlugin import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.Completable import com.intuit.player.jvm.core.bridge.format +import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeConfig import com.intuit.player.jvm.core.bridge.serialization.format.registerContextualSerializer import com.intuit.player.jvm.core.logger.TapableLogger -import com.intuit.player.jvm.core.player.* +import com.intuit.player.jvm.core.player.HeadlessPlayer +import com.intuit.player.jvm.core.player.Player +import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.core.player.state.CompletedState import com.intuit.player.jvm.core.player.state.PlayerFlowState +import com.intuit.player.jvm.core.player.state.inProgressState import com.intuit.player.jvm.core.plugins.LoggerPlugin import com.intuit.player.jvm.core.plugins.Plugin import com.intuit.player.jvm.core.plugins.findPlugin import com.intuit.player.plugins.beacon.BeaconPlugin import com.intuit.player.plugins.coroutines.FlowScopePlugin import com.intuit.player.plugins.pubsub.PubSubPlugin +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* -import kotlinx.serialization.modules.plus import kotlin.reflect.KClass +public typealias AndroidPlayerConfig = AndroidPlayer.Config + /** * [android.view.View] [Player] that is backed by another [Player]. * If no backing [Player] is supplied, it will build a self-contained @@ -37,12 +48,13 @@ import kotlin.reflect.KClass public class AndroidPlayer private constructor( private val player: HeadlessPlayer, override val plugins: List = player.plugins, -) : Player() { +) : Player(), ScriptSourceProvider by player.runtime as? ScriptSourceProvider ?: UnsupportedScriptProvider(player.runtime) { /** Convenience constructor to provide vararg style [plugins] parameter */ public constructor( vararg plugins: Plugin, - ) : this(plugins.toList()) + config: Config = Config(), + ) : this(plugins.toList(), config) /** * Constructor that takes a [context] and collection of [Plugin]s. @@ -52,7 +64,8 @@ public class AndroidPlayer private constructor( */ public constructor( plugins: List, - ) : this(HeadlessPlayer(*plugins.injectDefaultPlugins().toTypedArray())) + config: Config = Config(), + ) : this(HeadlessPlayer(plugins.injectDefaultPlugins(), config = config)) /** * Allow the [AndroidPlayer] to be built on top of a pre-existing @@ -84,16 +97,43 @@ public class AndroidPlayer private constructor( public fun call(context: Context): Context = super.call( context, { f, acc, hookCtx -> f(hookCtx, acc) }, - { f, hookCtx -> f(hookCtx, context) } + { f, hookCtx -> f(hookCtx, context) }, ) } + + internal class RecycleHook : SyncHook<(HookContext) -> Unit>() { + public fun call(): Unit = super.call { f, context -> + f(context) + } + } + + internal class ReleaseHook : SyncHook<(HookContext) -> Unit>() { + public fun call(): Unit = super.call { f, context -> + f(context) + } + } + + public class UpdateHook : SyncBailHook<(RenderableAsset?) -> BailResult, Unit>() { + public fun call(asset: RenderableAsset?, default: (HookContext) -> Unit): Unit? = super.call( + { f, _ -> + f(asset) + }, + default, + ) + } + public val context: ContextHook = ContextHook() + public val update: UpdateHook = UpdateHook() + internal val recycle: RecycleHook = RecycleHook() + internal val release: ReleaseHook = ReleaseHook() } override val hooks: Hooks = Hooks(player.hooks) override val state: PlayerFlowState by player::state + override val scope: CoroutineScope by player::scope + override fun start(flow: String): Completable = player.start(flow) private val assetSerializer = RenderableAsset.Serializer(this) @@ -115,8 +155,9 @@ public class AndroidPlayer private constructor( /** Helper provided to reduce overhead for asset registrations with metaData */ public fun registerAsset(klass: KClass, props: Map, factory: (AssetContext) -> RenderableAsset) { assetRegistry.register(props, factory) - if (player.format.serializersModule.getContextual(klass) == null) + if (player.format.serializersModule.getContextual(klass) == null) { player.format.registerContextualSerializer(klass, assetSerializer.conform(klass)) + } } /** Apply [AndroidPlayerPlugin]s last */ @@ -139,7 +180,16 @@ public class AndroidPlayer private constructor( transition.value = true clearCaches() view?.hooks?.onUpdate?.tap { asset -> - assetHandler(expandAsset(asset), transition.value) + try { + expandAsset(asset).let { expandedAsset -> + hooks.update.call(expandedAsset) { + assetHandler(expandedAsset, transition.value) + } + } + } catch (exception: Exception) { + logger.error("Error while expanding ${asset?.id}", exception) + inProgressState?.fail(PlayerException("Error while expanding ${asset?.id}", exception)) + } } } } @@ -207,13 +257,18 @@ public class AndroidPlayer private constructor( * prevent a leak. */ public fun recycle() { + // TODO: Remove this check by enhancing TapableLogger to out-last Player lifecycle to use default + if (!player.runtime.isReleased()) logger.debug("AndroidPlayer: recycling player") clearCaches() - // TODO: Allow [AndroidPlayerPlugins] the chance to "recycle" too + hooks.recycle.call() } override fun release() { + // TODO: Remove this check by enhancing TapableLogger to out-last Player lifecycle to use default + if (!player.runtime.isReleased()) player.logger.debug("AndroidPlayer: releasing player") clearCaches() player.release() + hooks.release.call() } /** @@ -252,8 +307,16 @@ public class AndroidPlayer private constructor( /** Helper to add default plugins if there isn't already an instance of that plugin */ private fun List.injectDefaultPlugins() = buildDefaultPlugins() .fold(this) { plugins, (defaultPluginClass, defaultPlugin) -> - if (plugins.filterIsInstance(defaultPluginClass).isEmpty()) plugins + defaultPlugin - else plugins + if (plugins.filterIsInstance(defaultPluginClass).isEmpty()) { + plugins + defaultPlugin + } else { + plugins + } } } + + public data class Config( + override var debuggable: Boolean = false, + override var coroutineExceptionHandler: CoroutineExceptionHandler? = null, + ) : PlayerRuntimeConfig() } diff --git a/android/player/src/main/java/com/intuit/player/android/AssetContext.kt b/android/player/src/main/java/com/intuit/player/android/AssetContext.kt index adef23107..c8a573585 100644 --- a/android/player/src/main/java/com/intuit/player/android/AssetContext.kt +++ b/android/player/src/main/java/com/intuit/player/android/AssetContext.kt @@ -51,12 +51,16 @@ public fun AssetContext.withContext(context: Context): AssetContext = copy(conte public fun AssetContext.withStyles(@StyleRes vararg styles: Style?): AssetContext = withStyles(styles.filterNotNull()) /** Create a new, styled [AssetContext] */ -public fun AssetContext.withStyles(@StyleRes styles: Styles): AssetContext = if (styles.isEmpty()) this else copy( - context = context?.let { player.getCachedStyledContext(it, styles) } ?: run { - val error = PlayerException("Android context not found! Ensure the asset is rendered with a valid Android context.") - player.inProgressState?.fail(error) - throw error - } -) +public fun AssetContext.withStyles(@StyleRes styles: Styles): AssetContext = if (styles.isEmpty()) { + this +} else { + copy( + context = context?.let { player.getCachedStyledContext(it, styles) } ?: run { + val error = PlayerException("Android context not found! Ensure the asset is rendered with a valid Android context.") + player.inProgressState?.fail(error) + throw error + }, + ) +} public fun AssetContext.build(): RenderableAsset = factory(this) diff --git a/android/player/src/main/java/com/intuit/player/android/asset/DecodableAsset.kt b/android/player/src/main/java/com/intuit/player/android/asset/DecodableAsset.kt index 0793f3841..e1974f92c 100644 --- a/android/player/src/main/java/com/intuit/player/android/asset/DecodableAsset.kt +++ b/android/player/src/main/java/com/intuit/player/android/asset/DecodableAsset.kt @@ -2,15 +2,22 @@ package com.intuit.player.android.asset import com.intuit.player.android.AssetContext import com.intuit.player.jvm.core.player.PlayerException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException /** Extension of [RenderableAsset] that provides data decoding through the [serializer] */ @Suppress("DEPRECATION_ERROR") public abstract class DecodableAsset(assetContext: AssetContext, private val serializer: KSerializer) : RenderableAsset(assetContext) { + /** Suspendable way to deserialize an instance of [Data] */ + public suspend fun getData(): Data = withContext(Dispatchers.Default) { + data + } + @Deprecated("Direct access to this property encourages blocking runtime access, use suspendable getData instead to ensure threads aren't blocked on data decoding.", ReplaceWith("getData()")) /** Instance of [Data] is passed to [hydrate] */ - public val data: Data by lazy { + public open val data: Data by lazy { try { asset.deserialize(serializer) } catch (exception: SerializationException) { diff --git a/android/player/src/main/java/com/intuit/player/android/asset/RenderableAsset.kt b/android/player/src/main/java/com/intuit/player/android/asset/RenderableAsset.kt index cc79dfb29..3c97b63a3 100644 --- a/android/player/src/main/java/com/intuit/player/android/asset/RenderableAsset.kt +++ b/android/player/src/main/java/com/intuit/player/android/asset/RenderableAsset.kt @@ -3,11 +3,16 @@ package com.intuit.player.android.asset import android.content.Context import android.view.View import androidx.annotation.StyleRes -import com.intuit.player.android.* +import com.intuit.player.android.AndroidPlayer +import com.intuit.player.android.AssetContext import com.intuit.player.android.DEPRECATED_WITH_DECODABLEASSET +import com.intuit.player.android.build import com.intuit.player.android.extensions.Style import com.intuit.player.android.extensions.Styles import com.intuit.player.android.extensions.removeSelf +import com.intuit.player.android.withContext +import com.intuit.player.android.withStyles +import com.intuit.player.android.withTag import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.asset.AssetWrapper import com.intuit.player.jvm.core.bridge.Node @@ -31,6 +36,8 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlin.reflect.KClass +internal typealias CachedAssetView = Pair + /** * [RenderableAsset] is the base class for each asset in an asset tree. * It is the second stage in the transform process. It's most important @@ -60,7 +67,7 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { * Helper to get the current cached [AssetContext] and [View]. * Will return empty pair if not found. */ - private val cachedAssetView get() = + internal val cachedAssetView: CachedAssetView get() = player.getCachedAssetView(assetContext) ?: cachedAssetViewNotFound /** Main API */ @@ -88,6 +95,12 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { get() = player.getCachedHydrationScope(assetContext) set(value) = player.cacheHydrationScope(assetContext, value) + internal fun renewHydrationScope(message: String): CoroutineScope { + _hydrationScope?.cancel(message) + _hydrationScope = player.subScope() + return hydrationScope + } + /** * Construct a [View] that represents the asset. * @@ -100,11 +113,14 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { requireContext() when { // View not found. Create and hydrate. - cachedView == null -> initView().also(::rehydrate) - // View found, but contexts are out of sync. Remove cached view and create and hydrate. + cachedView == null -> { + renewHydrationScope("recreating view") + initView().also { it.hydrate() } + } // View found, but contexts are out of sync. Remove cached view and create and hydrate. cachedAssetContext?.context != context || cachedAssetContext?.asset?.type != asset.type -> { + renewHydrationScope("recreating view") cachedView.removeSelf() - initView().also(::rehydrate) + initView().also { it.hydrate() } } // View found, but assets are out of sync. Rehydrate. It is possible for the hydrate // implementation to throw [StaleViewException] to signify that the view is out of sync. @@ -120,7 +136,7 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { // View found, everything is in sync. Do nothing. else -> cachedView } - }.also { player.cacheAssetView(assetContext, it) } + }.also { if (it !is SuspendableAsset.AsyncViewStub) player.cacheAssetView(assetContext, it) } /** Invalidate view, causing a complete re-render of the current asset */ public fun invalidateView() { @@ -128,6 +144,12 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { throw StaleViewException(assetContext) } + /** Private helper for managing scope for hydration */ + private fun rehydrate(view: View) { + renewHydrationScope("rehydrating ${asset.id}") + view.hydrate() + } + /** Instruct a [RenderableAsset] to [rehydrate] */ public fun rehydrate(): Unit = cachedAssetView.let { (_, view) -> try { @@ -137,16 +159,13 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { } } - /** Private helper for managing scope for hydration */ - private fun rehydrate(view: View) { - _hydrationScope?.cancel("rehydrating ${asset.id}") - _hydrationScope = player.subScope() - view.hydrate() - } - /** * Render the asset using the resulting [Context] of the [AndroidPlayer.Hooks.ContextHook] * called with the provided [context]. + * + * This should only be called from the Activity/Fragment to provide a [context] for [RenderableAsset]s to render with. + * Rendering of nested children assets should instead invoke the contextual [RenderableAsset.render] methods + * to automatically pull [context] from their parents. */ public fun render(context: Context): View = assetContext .withContext(player.hooks.context.call(context)) @@ -235,7 +254,7 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { action: String, element: String, asset: Asset = this.asset, - data: Any? = null + data: Any? = null, ): Unit = player.beacon(action, element, asset, data) public fun requireContext(): Context = context ?: run { @@ -270,13 +289,17 @@ public constructor(public val assetContext: AssetContext) : NodeWrapper { throw SerializationException("DecodableAsset.Serializer.serialize is not supported") /** Conform this [Serializer] to cast the expanded asset to [T] */ - public inline fun conform(): KSerializer = object : KSerializer by this as KSerializer { - override fun deserialize(decoder: Decoder): T = this@Serializer.deserialize(decoder) as T - } - - public fun conform(klass: KClass): KSerializer = object : KSerializer by this as KSerializer { - override fun deserialize(decoder: Decoder): T = klass.javaObjectType.cast(this@Serializer.deserialize(decoder))!! - } + public inline fun conform(): KSerializer = object : KSerializer by this as KSerializer { + override fun deserialize(decoder: Decoder) = this@Serializer.deserialize(decoder) as? T + } as KSerializer + + public fun conform(klass: KClass): KSerializer = object : KSerializer by this as KSerializer { + override fun deserialize(decoder: Decoder) = try { + klass.javaObjectType.cast(this@Serializer.deserialize(decoder)) + } catch (e: ClassCastException) { + null + } + } as KSerializer } // Seemingly needed to prevent stack overflow: https://github.com/Kotlin/kotlinx.serialization/issues/1776 diff --git a/android/player/src/main/java/com/intuit/player/android/asset/SuspendableAsset.kt b/android/player/src/main/java/com/intuit/player/android/asset/SuspendableAsset.kt new file mode 100644 index 000000000..7798af6a7 --- /dev/null +++ b/android/player/src/main/java/com/intuit/player/android/asset/SuspendableAsset.kt @@ -0,0 +1,131 @@ +package com.intuit.player.android.asset + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Canvas +import android.util.AttributeSet +import android.view.View +import android.view.View.OnAttachStateChangeListener +import android.view.ViewGroup +import com.intuit.player.android.AssetContext +import com.intuit.player.android.R +import com.intuit.player.jvm.core.player.PlayerException +import com.intuit.player.jvm.core.player.state.inProgressState +import com.intuit.player.jvm.core.utils.InternalPlayerApi +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlinx.serialization.KSerializer +import kotlin.coroutines.cancellation.CancellationException +import kotlin.coroutines.coroutineContext + +/** Extension of [DecodableAsset] that provides suspendable [initView] and [hydrate] APIs that will provide an instance of [Data] to use during [View] updates */ +public abstract class SuspendableAsset(assetContext: AssetContext, serializer: KSerializer) : DecodableAsset(assetContext, serializer) { + + // To be launched in Dispatchers.Default + public abstract suspend fun initView(data: Data): View + + final override fun initView(): View = AsyncViewStub( + hydrationScope, + hydrationScope.async { doInitView() }, + requireContext(), + ) { doHydrate(); player.cacheAssetView(assetContext, this) } + + private suspend fun doInitView() = withContext(Dispatchers.Default) { + initView(getData()).apply { setTag(R.bool.view_hydrated, false) } + } + + // To be launched in Dispatchers.Main + public abstract suspend fun View.hydrate(data: Data) + + final override fun View.hydrate() { + if (this is AsyncViewStub) return + + setTag(R.bool.view_hydrated, false) + hydrationScope.launch(Dispatchers.Main) { doHydrate() } + } + + private suspend fun View.doHydrate() = withContext(Dispatchers.Main) { + try { + hydrate(getData()) + setTag(R.bool.view_hydrated, true) + } catch (exception: StaleViewException) { + // b/c we're launched in a scope that isn't cared about anymore, we can't appropriately handle this, so just fast fail + player.inProgressState?.fail(PlayerException("SuspendableAssets can't appropriately handle invalidateViews currently, this should be handled in a future major", exception)) + } + } + + /** ViewStub derivative that will replace itself in the view tree once the [view] has resolved */ + @SuppressLint("ViewConstructor") + @InternalPlayerApi + public class AsyncViewStub @JvmOverloads constructor( + private val scope: CoroutineScope, + private val view: Deferred, + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0, + private val onView: suspend View.() -> Unit = {}, + ) : View(context, attrs, defStyle), OnAttachStateChangeListener { + + private val hydratedView: Deferred = scope.async { + view.await().also { onView(it) } + } + + init { + addOnAttachStateChangeListener(this) + } + + /** Suspend until there is a hydrated view, or returns null if the provided [scope] is cancelled */ + public suspend fun awaitView(): View? = try { + hydratedView.await() + } catch (e: CancellationException) { + // if it was the calling scope that is cancelled, this will re-raise + coroutineContext.ensureActive() + null + } + + /** Callback handler for when there is a hydrated view */ + public fun onView(handler: (View) -> Unit) { + scope.launch { + awaitView()?.let(handler) + } + } + + override fun onViewAttachedToWindow(v: View) { + scope.launch(Dispatchers.Main) { + awaitView()?.let(::replaceSelfWithView) + } + } + + override fun onViewDetachedFromWindow(v: View) {} + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(0, 0) + } + + @SuppressLint("MissingSuperCall") + override fun onAttachedToWindow() {} + + @SuppressLint("MissingSuperCall") + override fun draw(canvas: Canvas?) {} + + override fun dispatchDraw(canvas: Canvas?) {} + + private fun replaceSelfWithView(view: View) { + val parent = parent as? ViewGroup ?: return + val index = parent.indexOfChild(this) + parent.removeViewInLayout(this) + val layoutParams = layoutParams + if (layoutParams != null) { + parent.addView(view, index, layoutParams) + } else { + parent.addView(view, index) + } + removeOnAttachStateChangeListener(this) + } + } +} diff --git a/android/player/src/main/java/com/intuit/player/android/debug/UnsupportedScriptProvider.kt b/android/player/src/main/java/com/intuit/player/android/debug/UnsupportedScriptProvider.kt new file mode 100644 index 000000000..2eb16c5e5 --- /dev/null +++ b/android/player/src/main/java/com/intuit/player/android/debug/UnsupportedScriptProvider.kt @@ -0,0 +1,14 @@ +package com.intuit.player.android.debug + +import com.alexii.j2v8debugger.ScriptSourceProvider +import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.player.PlayerException + +internal class UnsupportedScriptProvider(private val runtime: Runtime<*>) : ScriptSourceProvider { + override val allScriptIds: Collection + get() = throw PlayerException("Unsupported exception, $runtime runtime does not support JS debugging") + + override fun getSource(scriptId: String): String { + throw PlayerException("Unsupported exception, $runtime runtime does not support JS debugging") + } +} diff --git a/android/player/src/main/java/com/intuit/player/android/extensions/into.kt b/android/player/src/main/java/com/intuit/player/android/extensions/Into.kt similarity index 84% rename from android/player/src/main/java/com/intuit/player/android/extensions/into.kt rename to android/player/src/main/java/com/intuit/player/android/extensions/Into.kt index 67eeac788..ae981dc05 100644 --- a/android/player/src/main/java/com/intuit/player/android/extensions/into.kt +++ b/android/player/src/main/java/com/intuit/player/android/extensions/Into.kt @@ -6,6 +6,12 @@ import android.widget.FrameLayout import androidx.core.view.children import androidx.transition.Transition import androidx.transition.TransitionManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +internal suspend infix fun View?.intoOnMain(root: FrameLayout) = withContext(Dispatchers.Main) { + into(root) +} /** * Helper method to replace the existing [FrameLayout] child @@ -36,7 +42,10 @@ public infix fun View?.into(root: FrameLayout) { public fun View?.transitionInto(root: FrameLayout, transition: Transition?) { root.removeAllViews() if (this == null) { - root.visibility = View.GONE + if (root.visibility != View.GONE) { + root.visibility = View.GONE + root.removeAllViews() + } } else { root.visibility = View.VISIBLE if (!root.children.contains(this)) { @@ -78,8 +87,10 @@ public infix fun View?.into(root: ViewGroup) { public infix fun List.into(root: ViewGroup) { val filtered = filterNotNull() if (filtered.isEmpty()) { - root.visibility = View.GONE - root.removeAllViews() + if (root.visibility != View.GONE) { + root.visibility = View.GONE + root.removeAllViews() + } } else { root.visibility = View.VISIBLE @@ -98,6 +109,6 @@ public infix fun List.into(root: ViewGroup) { } // Remove any leftover views - while (root.childCount > size) { root.removeViewAt(root.childCount - 1) } + while (root.childCount > filtered.size) { root.removeViewAt(root.childCount - 1) } } } diff --git a/android/player/src/main/java/com/intuit/player/android/extensions/overlayStyles.kt b/android/player/src/main/java/com/intuit/player/android/extensions/OverlayStyles.kt similarity index 93% rename from android/player/src/main/java/com/intuit/player/android/extensions/overlayStyles.kt rename to android/player/src/main/java/com/intuit/player/android/extensions/OverlayStyles.kt index 522785aaf..d15f90ef8 100644 --- a/android/player/src/main/java/com/intuit/player/android/extensions/overlayStyles.kt +++ b/android/player/src/main/java/com/intuit/player/android/extensions/OverlayStyles.kt @@ -22,7 +22,7 @@ private fun Style?.toStyles() = this?.let { public fun Context.overlayStyles( @StyleRes vararg additionalStyles: Style, - @StyleRes baseStyle: Style? + @StyleRes baseStyle: Style?, ): Context = overlayStyles(baseStyle.toStyles(), additionalStyles.toList()) public fun Context.overlayStyles(@StyleRes vararg additionalStyles: Style): Context = @@ -37,6 +37,6 @@ public fun Context.overlayStyles(@StyleRes vararg additionalStyles: Style): Cont */ public fun Context.overlayStyles( @StyleRes baseStyles: Styles = emptyList(), - @StyleRes additionalStyles: Styles = emptyList() + @StyleRes additionalStyles: Styles = emptyList(), ): Context = (baseStyles + additionalStyles) .fold(this, ::ContextThemeWrapper) diff --git a/android/player/src/main/java/com/intuit/player/android/extensions/removeSelf.kt b/android/player/src/main/java/com/intuit/player/android/extensions/RemoveSelf.kt similarity index 100% rename from android/player/src/main/java/com/intuit/player/android/extensions/removeSelf.kt rename to android/player/src/main/java/com/intuit/player/android/extensions/RemoveSelf.kt diff --git a/android/player/src/main/java/com/intuit/player/android/lifecycle/ManagedPlayerState.kt b/android/player/src/main/java/com/intuit/player/android/lifecycle/ManagedPlayerState.kt index f3b47be69..8b462e258 100644 --- a/android/player/src/main/java/com/intuit/player/android/lifecycle/ManagedPlayerState.kt +++ b/android/player/src/main/java/com/intuit/player/android/lifecycle/ManagedPlayerState.kt @@ -10,12 +10,16 @@ import com.intuit.player.jvm.core.player.state.PlayerFlowState public sealed class ManagedPlayerState { /** Initial state of the [PlayerViewModel] */ public object NotStarted : ManagedPlayerState() + /** State that represents any error encountered when instantiating a player, retrieving a flow, or running a flow */ public data class Error(public val exception: Exception) : ManagedPlayerState() + /** [Pending] represents the time spent retrieving a flow, either before any flow or after the [AndroidPlayer] reaches the [CompletedState] */ public object Pending : ManagedPlayerState() + /** State containing the current [asset] representation of the current in-progress flow */ public data class Running(public val asset: RenderableAsset?, public val animateViewTransition: Boolean) : ManagedPlayerState() + /** The [PlayerViewModel] reaches the [Done] state once the [PlayerViewModel.manager] has no more flows to produce */ public data class Done(public val completedState: CompletedState?) : ManagedPlayerState() diff --git a/android/player/src/main/java/com/intuit/player/android/lifecycle/PlayerViewModel.kt b/android/player/src/main/java/com/intuit/player/android/lifecycle/PlayerViewModel.kt index 5d0b553a6..7b298c372 100644 --- a/android/player/src/main/java/com/intuit/player/android/lifecycle/PlayerViewModel.kt +++ b/android/player/src/main/java/com/intuit/player/android/lifecycle/PlayerViewModel.kt @@ -13,16 +13,34 @@ import com.intuit.player.jvm.core.managed.AsyncFlowIterator import com.intuit.player.jvm.core.managed.AsyncIterationManager import com.intuit.player.jvm.core.managed.FlowManager import com.intuit.player.jvm.core.player.PlayerException -import com.intuit.player.jvm.core.player.state.* +import com.intuit.player.jvm.core.player.state.CompletedState +import com.intuit.player.jvm.core.player.state.ErrorState +import com.intuit.player.jvm.core.player.state.InProgressState +import com.intuit.player.jvm.core.player.state.NotStartedState +import com.intuit.player.jvm.core.player.state.ReleasedState +import com.intuit.player.jvm.core.player.state.completedState +import com.intuit.player.jvm.core.player.state.inProgressState import com.intuit.player.jvm.core.plugins.Plugin import com.intuit.player.jvm.core.plugins.RuntimePlugin import com.intuit.player.plugins.beacon.onBeacon -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking /** * Android lifecycle-aware player manager that integrates and manages - * state between a [FlowManager] and an [AndroidPlayer]. This sta + * state between a [FlowManager] and an [AndroidPlayer]. * * As-is, this bare-bones implementation does not include any additional * plugins, meaning that any flows will not actually expand into @@ -45,21 +63,29 @@ public open class PlayerViewModel(flows: AsyncFlowIterator) : ViewModel(), Andro */ protected open val plugins: List = emptyList() - // TODO: accessing non-final fields in constructor -- must not use player before init is done - // This could be fixed by requiring [plugins] be in the constructor, but this is kinda - // backwards since subclasses are required to configure plugin behavior manually. Maybe - // apps just supply a meta plugin to configure things? Although, the reason for configuring - // plugins here is to potentially hook into the fragment. External actions is a great - // example, where the app needs to actually load a different native experience outside the - // player scope. Not sure how to solve this. Maybe a player factory instead that requires - // the [PlayerViewModel] to be finished initialization? - protected val player: AndroidPlayer by lazy { - AndroidPlayer(plugins + this) + protected open val config: AndroidPlayer.Config = AndroidPlayer.Config() + + @ExperimentalPlayerApi + public val deferredPlayer: Deferred = viewModelScope.async(Dispatchers.Default) { + // this is unfortunate, but is essentially for ensuring view model has completely initialized + while (plugins == null) { delay(5) } + AndroidPlayer(plugins + this@PlayerViewModel, config) + } + + @OptIn(ExperimentalCoroutinesApi::class, ExperimentalPlayerApi::class) + public val player: AndroidPlayer by lazy { + if (deferredPlayer.isCompleted) { + deferredPlayer.getCompleted() + } else { + runBlocking { + deferredPlayer.await() + } + } } protected val manager: FlowManager = FlowManager(flows) - private lateinit var runtime: Runtime<*> + private var runtime: Runtime<*>? = null private var _state = MutableStateFlow(ManagedPlayerState.NotStarted) private val _beacons = MutableSharedFlow() @@ -69,7 +95,7 @@ public open class PlayerViewModel(flows: AsyncFlowIterator) : ViewModel(), Andro init { // next() TODO: If we fix the non-final field error, we can prefetch here - viewModelScope.launch { + viewModelScope.launch(Dispatchers.Default) { manager.state.collect { when (it) { AsyncIterationManager.State.NotStarted -> _state.emit(ManagedPlayerState.NotStarted) @@ -86,11 +112,11 @@ public open class PlayerViewModel(flows: AsyncFlowIterator) : ViewModel(), Andro when { it.isSuccess -> player.logger.info( "Flow completed successfully!", - it.getOrNull()?.endState + it.getOrNull()?.endState, ) it.isFailure -> player.logger.error( "Error in Flow!", - it.exceptionOrNull()?.stackTraceToString() + it.exceptionOrNull(), ) } } @@ -114,6 +140,7 @@ public open class PlayerViewModel(flows: AsyncFlowIterator) : ViewModel(), Andro // which will either start a new flow or transition to done is CompletedState -> manager.next(state) is ErrorState -> _state.tryEmit(ManagedPlayerState.Error(state.error)) + is InProgressState, ReleasedState, null -> Unit } } } @@ -122,20 +149,21 @@ public open class PlayerViewModel(flows: AsyncFlowIterator) : ViewModel(), Andro this.runtime = runtime } - override fun onCleared() { - runtime.scope.launch { - if (manager.state.value != AsyncIterationManager.State.Done) manager.iterator.terminate() - release() + public override fun onCleared() { + if (manager.state.value != AsyncIterationManager.State.Done) { + runBlocking { + manager.iterator.terminate() + } } + + release() } public fun recycle() { - player.logger.debug("PlayerViewModel: recycling player") player.recycle() } public fun release() { - player.logger.debug("PlayerViewModel: releasing player") player.release() } @@ -153,12 +181,19 @@ public open class PlayerViewModel(flows: AsyncFlowIterator) : ViewModel(), Andro when (state.value) { ManagedPlayerState.NotStarted -> manager.next() is ManagedPlayerState.Error, - is ManagedPlayerState.Running -> when (val currentFlow = manager.state.value) { + is ManagedPlayerState.Running, + -> when (manager.state.value) { AsyncIterationManager.State.NotStarted -> manager.next() - is AsyncIterationManager.State.Item<*> -> start(currentFlow.value as String) - // try to re-retrieve the next flow from the previous state - is AsyncIterationManager.State.Error -> manager.next(player.completedState) + is AsyncIterationManager.State.Item<*>, + is AsyncIterationManager.State.Error, + -> manager.next(player.completedState) + AsyncIterationManager.State.Done, + AsyncIterationManager.State.Pending, + -> Unit } + is ManagedPlayerState.Done, + ManagedPlayerState.Pending, + -> Unit } } @@ -174,7 +209,7 @@ public open class PlayerViewModel(flows: AsyncFlowIterator) : ViewModel(), Andro /** Generic [ViewModelProvider.AndroidViewModelFactory] to conveniently construct some [T] with an [Application] and [AsyncFlowIterator] */ public class Factory( private val iterator: AsyncFlowIterator, - private val factory: (AsyncFlowIterator) -> T = { i -> PlayerViewModel(i) as T } + private val factory: (AsyncFlowIterator) -> T = { i -> PlayerViewModel(i) as T }, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { diff --git a/android/player/src/main/java/com/intuit/player/android/registry/RegistryPlugin.kt b/android/player/src/main/java/com/intuit/player/android/registry/RegistryPlugin.kt index 20e188142..f719e13d3 100644 --- a/android/player/src/main/java/com/intuit/player/android/registry/RegistryPlugin.kt +++ b/android/player/src/main/java/com/intuit/player/android/registry/RegistryPlugin.kt @@ -1,6 +1,7 @@ package com.intuit.player.android.registry import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.plugins.JSPluginWrapper @@ -15,13 +16,13 @@ internal class RegistryPlugin : JSPluginWrapper { runtime.execute(readSource("plugins/partial-match-fingerprint/core/dist/partial-match-fingerprint-plugin.prod.js")) runtime.execute(readSource("core/partial-match-registry/dist/partial-match-registry.prod.js")) instance = runtime.execute( - """(new PartialMatchFingerprintPlugin.PartialMatchFingerprintPlugin(new Registry.Registry()))""" + """(new PartialMatchFingerprintPlugin.PartialMatchFingerprintPlugin(new Registry.Registry()))""", ) as Node } fun register(match: Map, value: T) { registry.add(value) - instance.getFunction("register")!!.invoke(match, registry.size - 1) + instance.getInvokable("register")!!.invoke(match, registry.size - 1) } /** @@ -31,7 +32,7 @@ internal class RegistryPlugin : JSPluginWrapper { */ operator fun get(id: String): T? = getAssetIndex(id)?.let(registry::getOrNull) - private fun getAssetIndex(id: String): Int? = instance.getFunction("get")!!.invoke(id) as? Int + private fun getAssetIndex(id: String): Int? = instance.getInvokable("get")!!.invoke(id) as? Int private fun readSource(source: String) = RegistryPlugin::class.java.classLoader!! .getResource(source) diff --git a/android/player/src/main/java/com/intuit/player/android/ui/PlayerFragment.kt b/android/player/src/main/java/com/intuit/player/android/ui/PlayerFragment.kt index 288bf30eb..6f6b81b3e 100644 --- a/android/player/src/main/java/com/intuit/player/android/ui/PlayerFragment.kt +++ b/android/player/src/main/java/com/intuit/player/android/ui/PlayerFragment.kt @@ -8,19 +8,27 @@ import android.view.ViewGroup import android.widget.ProgressBar import androidx.core.view.doOnLayout import androidx.fragment.app.Fragment +import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.lifecycleScope import androidx.transition.Transition import com.intuit.player.android.AndroidPlayer import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.asset.SuspendableAsset import com.intuit.player.android.databinding.DefaultFallbackBinding import com.intuit.player.android.databinding.FragmentPlayerBinding import com.intuit.player.android.extensions.into +import com.intuit.player.android.extensions.intoOnMain import com.intuit.player.android.extensions.transitionInto import com.intuit.player.android.lifecycle.ManagedPlayerState import com.intuit.player.android.lifecycle.PlayerViewModel import com.intuit.player.android.lifecycle.fail +import com.intuit.player.jvm.core.experimental.ExperimentalPlayerApi import com.intuit.player.jvm.core.managed.AsyncFlowIterator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** * [Fragment] wrapper integration with the [AndroidPlayer]. Delegates @@ -48,6 +56,14 @@ import kotlinx.coroutines.flow.collect */ public abstract class PlayerFragment : Fragment(), ManagedPlayerState.Listener { + /** [LifecycleCoroutineScope.launchWhenStarted] extension for waiting on [AndroidPlayer] instance */ + @ExperimentalPlayerApi + protected fun LifecycleCoroutineScope.launchWhenReady(block: suspend CoroutineScope.(player: AndroidPlayer) -> Unit) { + launchWhenStarted { + block(playerViewModel.deferredPlayer.await()) + } + } + private var _binding: FragmentPlayerBinding? = null /** @@ -65,36 +81,40 @@ public abstract class PlayerFragment : Fragment(), ManagedPlayerState.Listener { init { lifecycleScope.launchWhenStarted { - playerViewModel.state.collect { - when (it) { - ManagedPlayerState.NotStarted -> { - buildLoadingView() into binding.playerCanvas - onNotStarted() - } + // get the player view model on the main thread + val playerViewModel = playerViewModel + withContext(Dispatchers.Default) { + playerViewModel.state.collect { + when (it) { + ManagedPlayerState.NotStarted -> { + buildLoadingView() intoOnMain binding.playerCanvas + onNotStarted() + } - ManagedPlayerState.Pending -> { - buildLoadingView() into binding.playerCanvas - onPending() - } + ManagedPlayerState.Pending -> { + buildLoadingView() intoOnMain binding.playerCanvas + onPending() + } - is ManagedPlayerState.Running -> { - try { - handleAssetUpdate(it.asset, it.animateViewTransition) - onRunning(it) - } catch (exception: Exception) { - exception.printStackTrace() - playerViewModel.fail("Error rendering asset", exception) + is ManagedPlayerState.Running -> { + try { + handleAssetUpdate(it.asset, it.animateViewTransition) + onRunning(it) + } catch (exception: Exception) { + exception.printStackTrace() + playerViewModel.fail("Error rendering asset", exception) + } } - } - is ManagedPlayerState.Error -> { - buildFallbackView(it.exception) into binding.playerCanvas - onError(it) - } + is ManagedPlayerState.Error -> { + buildFallbackView(it.exception) intoOnMain binding.playerCanvas + onError(it) + } - is ManagedPlayerState.Done -> { - buildDoneView() into binding.playerCanvas - onDone(it) + is ManagedPlayerState.Done -> { + buildDoneView() intoOnMain binding.playerCanvas + onDone(it) + } } } } @@ -104,7 +124,7 @@ public abstract class PlayerFragment : Fragment(), ManagedPlayerState.Listener { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View = FragmentPlayerBinding.inflate(inflater, container, false).run { _binding = this root @@ -122,24 +142,46 @@ public abstract class PlayerFragment : Fragment(), ManagedPlayerState.Listener { playerViewModel.start() } + /** Default suspendable implementation of [handleAssetUpdate] */ + @ExperimentalPlayerApi + protected open suspend fun renderIntoPlayerCanvas(asset: RenderableAsset?, animateTransition: Boolean) { + val startTime = System.currentTimeMillis() + val view = asset?.render(requireContext())?.let { + // unwrap if we know we have an async view stub, and just wait on the actual view + if (it is SuspendableAsset.AsyncViewStub) it.awaitView() else it + } + + view?.doOnLayout { + playerViewModel.logRenderTime(asset, System.currentTimeMillis() - startTime) + } + + // swap to main + withContext(Dispatchers.Main) { + if (asset is RenderableAsset.ViewportAsset) binding.scrollContainer.isFillViewport = true + + animateTransition + .takeIf { it } + ?.let { binding.scrollContainer.scrollTo(0, 0) } + ?.let { buildTransitionAnimation() } + ?.let { view.transitionInto(binding.playerCanvas, it) } + ?: (view into binding.playerCanvas) + } + } + /** * Handle [asset] updates from the [PlayerViewModel]. By default, * this will invoke [RenderableAsset.render] with no additional * styles and inject that into the view tree. */ protected open fun handleAssetUpdate(asset: RenderableAsset?, animateTransition: Boolean) { - val startTime = System.currentTimeMillis() - if (asset is RenderableAsset.ViewportAsset) binding.scrollContainer.isFillViewport = true - val view = asset?.render(requireContext())?.also { - it.doOnLayout { playerViewModel.logRenderTime(asset, System.currentTimeMillis() - startTime) } - } - if (animateTransition) { - buildTransitionAnimation()?.let { - view.transitionInto(binding.playerCanvas, it) - return + lifecycleScope.launch(Dispatchers.Default) { + try { + renderIntoPlayerCanvas(asset, animateTransition) + } catch (exception: Exception) { + exception.printStackTrace() + playerViewModel.fail("Error rendering asset", exception) } } - view into binding.playerCanvas } public open fun buildTransitionAnimation(): Transition? = null diff --git a/android/player/src/main/res/values/tags.xml b/android/player/src/main/res/values/tags.xml new file mode 100644 index 000000000..2deaa9c28 --- /dev/null +++ b/android/player/src/main/res/values/tags.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/player/src/test/java/android/util/Log.kt b/android/player/src/test/java/android/util/Log.kt index b4f6a03dd..6c779bc31 100644 --- a/android/player/src/test/java/android/util/Log.kt +++ b/android/player/src/test/java/android/util/Log.kt @@ -2,37 +2,56 @@ package android.util -internal var e: String? = null +internal var e: MutableList = mutableListOf() internal fun e(tag: String, msg: String): Int { - e = "ERROR: $tag: $msg" - println(e) + val message = "ERROR: $tag: $msg" + e.add(message) + println(message) return 0 } -internal var w: String? = null +internal var w: MutableList = mutableListOf() internal fun w(tag: String, msg: String): Int { - w = "WARN: $tag: $msg" - println(w) + val message = "WARN: $tag: $msg" + w.add(message) + println(message) return 0 } -internal var i: String? = null +internal var i: MutableList = mutableListOf() internal fun i(tag: String, msg: String): Int { - i = "INFO: $tag: $msg" - println(i) + val message = "INFO: $tag: $msg" + i.add(message) + println(message) return 0 } -internal var d: String? = null +internal var d: MutableList = mutableListOf() internal fun d(tag: String, msg: String): Int { - d = "DEBUG: $tag: $msg" - println(d) + val message = "DEBUG: $tag: $msg" + d.add(message) + println(message) return 0 } -internal var t: String? = null -internal fun t(tag: String, msg: String): Int { - d = "DEBUG: $tag: $msg" - println(d) +internal var v: MutableList = mutableListOf() +internal fun v(tag: String, msg: String): Int { + val message = "TRACE: $tag: $msg" + v.add(message) + println(message) return 0 } + +internal fun clearLogs() = listOf(e, w, i, d, v).forEach { it.clear() } + +internal enum class Level { + Error, Warn, Info, Debug, Verbose; + + fun getLogs(): List = when (this) { + Error -> e + Warn -> w + Info -> i + Debug -> d + Verbose -> v + } +} diff --git a/android/player/src/test/java/com/intuit/player/android/AndroidPlayerTest.kt b/android/player/src/test/java/com/intuit/player/android/AndroidPlayerTest.kt index 0e9e684a6..0bf7af112 100644 --- a/android/player/src/test/java/com/intuit/player/android/AndroidPlayerTest.kt +++ b/android/player/src/test/java/com/intuit/player/android/AndroidPlayerTest.kt @@ -1,10 +1,12 @@ package com.intuit.player.android import android.content.Context +import com.intuit.player.android.asset.RenderableAsset import com.intuit.player.android.utils.SimpleAsset +import com.intuit.player.android.utils.TestAssetsPlugin import com.intuit.player.android.utils.awaitFirstView +import com.intuit.player.jvm.core.bridge.PlayerRuntimeException import com.intuit.player.jvm.core.player.HeadlessPlayer -import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.utils.start import com.intuit.player.jvm.utils.test.runBlockingTest import com.intuit.player.plugins.beacon.BeaconPlugin @@ -13,7 +15,12 @@ import com.intuit.player.plugins.pubsub.PubSubPlugin import com.intuit.player.plugins.pubsub.pubSubPlugin import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension -import org.junit.jupiter.api.Assertions.* +import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.Json +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith @@ -97,7 +104,7 @@ internal class AndroidPlayerTest { fun `release puts player in unusable state`() { val player = AndroidPlayer() player.release() - assertThrows { + assertThrows { player.start(SimpleAsset.sampleFlow) } } @@ -114,4 +121,18 @@ internal class AndroidPlayerTest { assertNull(player.getCachedAssetView(asset.assetContext)) assertNotNull(player.start(SimpleAsset.sampleFlow)) } + + @Test + fun `cannot encode a renderable asset`() = runBlockingTest { + val player = AndroidPlayer(TestAssetsPlugin) + val serializer = RenderableAsset.Serializer(player).conform() + player.registerAsset("simple", ::SimpleAsset) + val asset = player.awaitFirstView(SimpleAsset.sampleFlow)!! + assertEquals( + "DecodableAsset.Serializer.serialize is not supported", + assertThrows { + Json.encodeToString(serializer, asset) + }.message, + ) + } } diff --git a/android/player/src/test/java/com/intuit/player/android/AssetContextTest.kt b/android/player/src/test/java/com/intuit/player/android/AssetContextTest.kt index 7bf42a436..2c40c196f 100644 --- a/android/player/src/test/java/com/intuit/player/android/AssetContextTest.kt +++ b/android/player/src/test/java/com/intuit/player/android/AssetContextTest.kt @@ -2,14 +2,20 @@ package com.intuit.player.android import android.content.Context import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.utils.SimpleAsset import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.player.PlayerException +import com.intuit.player.jvm.core.player.state.ErrorState +import com.intuit.player.jvm.utils.start import io.mockk.every import io.mockk.mockk import io.mockk.spyk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows internal class AssetContextTest { private val context = spyk() @@ -59,4 +65,15 @@ internal class AssetContextTest { val newContext = assetContext.withTag("tag") assertEquals("some-id-tag", newContext.id) } + + @Test + fun `can't overlay styles without a context`() { + val player = AndroidPlayer().apply { + start(SimpleAsset.sampleFlow) + } + assertThrows { + AssetContext(null, asset, player, factory).withStyles(R.style.TextAppearance_AppCompat) + } + assertTrue(player.state is ErrorState) + } } diff --git a/android/player/src/test/java/com/intuit/player/android/extensions/CoroutineTestDispatcherExtension.kt b/android/player/src/test/java/com/intuit/player/android/extensions/CoroutineTestDispatcherExtension.kt new file mode 100644 index 000000000..91c0c3a63 --- /dev/null +++ b/android/player/src/test/java/com/intuit/player/android/extensions/CoroutineTestDispatcherExtension.kt @@ -0,0 +1,20 @@ +package com.intuit.player.android.extensions + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +@OptIn(ExperimentalCoroutinesApi::class) +public class CoroutineTestDispatcherExtension : AfterEachCallback, BeforeEachCallback { + + private val dispatcher = UnconfinedTestDispatcher() + + override fun beforeEach(context: ExtensionContext?): Unit = Dispatchers.setMain(dispatcher) + + override fun afterEach(context: ExtensionContext?): Unit = Dispatchers.resetMain() +} diff --git a/android/player/src/test/java/com/intuit/player/android/extensions/IntoKtTest.kt b/android/player/src/test/java/com/intuit/player/android/extensions/IntoKtTest.kt new file mode 100644 index 000000000..16db88ee7 --- /dev/null +++ b/android/player/src/test/java/com/intuit/player/android/extensions/IntoKtTest.kt @@ -0,0 +1,113 @@ +package com.intuit.player.android.extensions + +import android.view.View +import android.view.View.GONE +import android.view.ViewGroup +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import io.mockk.verify +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(MockKExtension::class) +internal class IntoKtTest { + + @MockK + lateinit var view1: View + + @MockK + lateinit var view2: View + + @MockK + lateinit var view3: View + + @MockK + lateinit var view4: View + + @MockK(relaxed = true) + lateinit var rootView: ViewGroup + + @BeforeEach + fun setup() { + every { view1.parent } answers { rootView } + every { view2.parent } answers { rootView } + every { view3.parent } answers { rootView } + every { view4.parent } answers { rootView } + } + + @Test + fun `the root view is hidden if the filtered collection is empty`() { + listOf(null, null, null) into rootView + verify { + rootView.visibility = GONE + rootView.removeAllViews() + } + } + + @Test + fun `views are inserted into the root view in the correct order`() { + listOf(view1, view2, view3) into rootView + verify { + rootView.addView(view1, 0) + rootView.addView(view2, 1) + rootView.addView(view3, 2) + } + } + + @Test + fun `views that are already in the root view and also in the new view are not inserted again`() { + mockChildIndices() + + listOf(view1, view2, view3) into rootView + verify(inverse = true) { + rootView.addView(view1, any()) + } + } + + @Test + fun `views that are not present in updated collection are removed`() { + var childCount = 3 + mockChildIndices() + mockChildCount { childCount } + every { rootView.removeViewAt(any()) } answers { childCount-- } + + listOf(view1, view2) into rootView + verify { rootView.removeViewAt(2) } + } + + @Test + fun `views that are not present in updated collection are removed even with null entries`() { + var childCount = 3 + mockChildIndices() + mockChildCount { childCount } + every { rootView.removeViewAt(any()) } answers { childCount-- } + + listOf(view1, view2, null) into rootView + verify { rootView.removeViewAt(2) } + } + + @Test + fun `views that are different have the old reference removed and the new one added`() { + mockChildIndices() + var childCount = 3 + every { rootView.removeViewAt(any()) } answers { childCount-- } + + listOf(view1, view2, view4) into rootView + verify { + rootView.removeView(view3) + rootView.addView(view4, 2) + } + } + + private fun mockChildIndices() { + every { rootView.getChildAt(0) } returns view1 + every { rootView.getChildAt(1) } returns view2 + every { rootView.getChildAt(2) } returns view3 + } + + private fun mockChildCount(childCount: () -> Int) { + every { rootView.childCount } answers { childCount() } + } +} diff --git a/android/player/src/test/java/com/intuit/player/android/lifecycle/PlayerViewModelTest.kt b/android/player/src/test/java/com/intuit/player/android/lifecycle/PlayerViewModelTest.kt new file mode 100644 index 000000000..6da79de44 --- /dev/null +++ b/android/player/src/test/java/com/intuit/player/android/lifecycle/PlayerViewModelTest.kt @@ -0,0 +1,262 @@ +package com.intuit.player.android.lifecycle + +import android.util.Level +import android.util.clearLogs +import com.intuit.player.android.utils.SimpleAsset +import com.intuit.player.jvm.core.bridge.PlayerRuntimeException +import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.managed.AsyncFlowIterator +import com.intuit.player.jvm.core.player.state.CompletedState +import com.intuit.player.jvm.core.player.state.ErrorState +import com.intuit.player.jvm.core.player.state.InProgressState +import com.intuit.player.jvm.core.player.state.PlayerFlowState +import com.intuit.player.jvm.core.player.state.ReleasedState +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestCoroutineScope +import kotlinx.coroutines.withTimeout +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(MockKExtension::class) +internal class PlayerViewModelTest { + + private val validFlow = "{\"id\": \"id\",\"navigation\": {\"BEGIN\": \"FLOW_1\",\"FLOW_1\": {\"startState\": \"END_Done\",\"END_Done\": {\"state_type\": \"END\",\"outcome\": \"done\"}}}}" + + private val invalidFlow = "{\"id\": \"id\",\"navigation\": {\"BEGIN\": \"FLOW\",\"FLOW_1\": {\"startState\": \"END_Done\",\"END_Done\": {\"state_type\": \"END\",\"outcome\": \"done\"}}}}" + + @MockK lateinit var flowIterator: AsyncFlowIterator + + @MockK lateinit var runtime: Runtime<*> + + lateinit var viewModel: PlayerViewModel + + // TODO: This likely doesn't need to happen if we can inject a test dispatcher effectively + private suspend fun suspendUntilCondition(timeout: Long = 5000, getValue: () -> T, condition: (T) -> Boolean, messageSupplier: (T) -> String): T = try { + withTimeout(timeout) { + var result: T = getValue() + while (!condition(result)) { + delay(50) + result = getValue() + } + + result + } + } catch (exception: TimeoutCancellationException) { + throw AssertionError(messageSupplier(getValue())) + } + + private suspend fun Level.assertLogged(value: String, times: Int = 1, timeout: Long = 5000) { + suspendUntilCondition( + timeout, + this::getLogs, + { it.filter { it == value }.size == times }, + { "$this log not captured: $value\nin ${getLogs()}" }, + ) + } + + private suspend inline fun assertPlayerState(timeout: Long = 5000): T = suspendUntilCondition( + timeout, + viewModel.player::state, + { it is T }, + { "Expected Player state to eventually be ${T::class}, but is ${viewModel.player.state}" }, + ) as T + + private suspend inline fun assertManagedPlayerState(timeout: Long = 5000): T = suspendUntilCondition( + timeout, + viewModel.state::value, + { it is T }, + { "Expected Managed Player state to eventually be ${T::class}, but is ${viewModel.state.value}" }, + ) as T + + @BeforeEach + fun setup() { + viewModel = PlayerViewModel(flowIterator) + every { runtime.scope } returns TestCoroutineScope() + // TODO: Change to StandardTestDispatcher + every { runtime.scope } returns CoroutineScope(Dispatchers.Default) + coEvery { flowIterator.terminate() } returns Unit + } + + @AfterEach + fun tearDown() = clearLogs() + + @Test + fun `test factory`() { + assertNotNull( + PlayerViewModel.Factory(flowIterator) { + PlayerViewModel(it) + }.create(PlayerViewModel::class.java), + ) + } + + @Test + fun `AndroidPlayer isn't null`() { + assertNotNull(viewModel.player) + } + + @Test + fun `apply android player onUpdate`() = runBlocking { + coEvery { flowIterator.next(any()) } returns SimpleAsset.sampleFlow.toString() + viewModel.start() + coVerify(exactly = 1) { flowIterator.next(any()) } + assertPlayerState() + Level.Warn.assertLogged("WARN: AndroidPlayer: Warning in flow: simple-asset of type simple is not registered") + } + + @Test + fun `apply android player state hook tap`() = runBlocking { + coEvery { flowIterator.next(any()) } returns validFlow andThen invalidFlow + viewModel.start() + coVerify(exactly = 1) { flowIterator.next(null) } + coVerify(exactly = 1) { flowIterator.next(any()) } + assertEquals("Error: No flow defined for: FLOW", assertPlayerState().error.message) + } + + @Test + fun `start calls next on manager`() { + coEvery { flowIterator.next(any()) } returns SimpleAsset.sampleFlow.toString() + viewModel.start() + coVerify(exactly = 1) { flowIterator.next(null) } + } + + @Test + fun `start eventually finishes if iterator does not return a new flow`() = runBlocking { + coEvery { flowIterator.next(any()) } returns null + viewModel.start() + val doneState = assertManagedPlayerState() + assertEquals(ManagedPlayerState.Done(null), doneState) + } + + @Test + fun `start happy path`() = runBlocking { + coEvery { flowIterator.next(any()) } returns validFlow andThen null + viewModel.start() + assertPlayerState() + Level.Info.assertLogged("INFO: AndroidPlayer: Flow completed successfully!, {state_type=END, outcome=done}") + } + + @Test + fun `start error path`() = runBlocking { + coEvery { flowIterator.next(any()) } returns invalidFlow + viewModel.start() + assertPlayerState() + Level.Error.assertLogged("ERROR: AndroidPlayer: Error in Flow!, {}") + Level.Error.assertLogged("ERROR: AndroidPlayer: Something went wrong: No flow defined for: FLOW") + } + + @Test + fun `start will emit error state if iterator errors out`() = runBlocking { + val exception = Exception("oh no") + coEvery { flowIterator.next(any()) } throws exception + viewModel.start() + val errorState = assertManagedPlayerState() + assertEquals("oh no", errorState.exception.message) + } + + @Test + fun `recycle calls into AndroidPlayer recycle`() { + var recycled = false + viewModel.player.hooks.recycle.tap("releaseTest") { + recycled = true + } + viewModel.recycle() + assertTrue(recycled) + } + + @Test + fun `onCleared releases player`() = runBlocking { + coEvery { flowIterator.terminate() } returns Unit + viewModel.apply(runtime) + viewModel.onCleared() + assertPlayerState() + assertEquals( + "[J2V8] Runtime object has been released!", + assertThrows { + viewModel.player.start(SimpleAsset.sampleFlow.toString()) + }.message, + ) + } + + @Test + fun `release clears player cache and releases runtime`() { + var released = false + viewModel.player.hooks.release.tap("releaseTest") { + released = true + } + coEvery { flowIterator.next(null) } returns SimpleAsset.sampleFlow.toString() + viewModel.release() + assertThrows { + viewModel.player.start(SimpleAsset.sampleFlow.toString()) + } + assertTrue(released) + } + + @Test + fun `test fail`() = runBlocking { + val exception = Exception("oh no") + coEvery { flowIterator.next(any()) } returns SimpleAsset.sampleFlow.toString() + viewModel.start() + + assertPlayerState() + + viewModel.fail("extension fail", exception) + assertEquals("extension fail", assertPlayerState().error.message) + } + + @Test + fun `retry should start player if not started`() { + coEvery { flowIterator.next(any()) } returns SimpleAsset.sampleFlow.toString() + viewModel.retry() + coVerify(exactly = 1) { flowIterator.next(null) } + } + + @Test + fun `retry should call manager next if it's running`() = runBlocking { + coEvery { flowIterator.next(any()) } returns SimpleAsset.sampleFlow.toString() + viewModel.start() + + assertManagedPlayerState() + + viewModel.retry() + coVerify(exactly = 2) { flowIterator.next(null) } + } + + @Test + fun `retry should call manager next if it's in error state`() = runBlocking { + val exception = Exception("oh no") + coEvery { flowIterator.next(any()) } throws exception + viewModel.start() + + assertManagedPlayerState() + + viewModel.retry() + coVerify(exactly = 2) { flowIterator.next(null) } + } + + @Test + fun `view model can be cleared successfully if player is never used`() = runBlocking { + val exception = Exception("oh no") + coEvery { flowIterator.next(any()) } throws exception + viewModel.start() + + assertManagedPlayerState() + + // ensures safe handling during cleanup when player is never instantiated + viewModel.onCleared() + } +} diff --git a/android/player/src/test/java/com/intuit/player/android/logger/AndroidLoggerTest.kt b/android/player/src/test/java/com/intuit/player/android/logger/AndroidLoggerTest.kt index 31023a9fc..7966f9e6d 100644 --- a/android/player/src/test/java/com/intuit/player/android/logger/AndroidLoggerTest.kt +++ b/android/player/src/test/java/com/intuit/player/android/logger/AndroidLoggerTest.kt @@ -1,15 +1,17 @@ package com.intuit.player.android.logger +import android.util.clearLogs import android.util.d import android.util.e import android.util.i -import android.util.t +import android.util.v import android.util.w +import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test internal class AndroidLoggerTest { - companion object { const val defaultTag = "AndroidLogger" const val trace = "this is very verbose" @@ -19,10 +21,15 @@ internal class AndroidLoggerTest { const val error = "this is an error" } + @AfterEach + fun tearDown() = clearLogs() + @Test fun `test log methods`() { val logger = AndroidLogger() assertEquals(defaultTag, logger.name) + logger.trace(trace) + assertTrace() logger.debug(debug) assertDebug() logger.info(info) @@ -38,6 +45,8 @@ internal class AndroidLoggerTest { val tag = "Logger" val logger = AndroidLogger(tag) assertEquals(tag, logger.name) + logger.trace(trace) + assertTrace(tag) logger.debug(debug) assertDebug(tag) logger.info(info) @@ -49,20 +58,17 @@ internal class AndroidLoggerTest { } private fun assertTrace(tag: String = defaultTag, msg: String = trace) = - t.assertLogged("TRACE", tag, msg) + v.assertLogged("TRACE", tag, msg) private fun assertDebug(tag: String = defaultTag, msg: String = debug) = d.assertLogged("DEBUG", tag, msg) - private fun assertInfo(tag: String = defaultTag, msg: String = info) = i.assertLogged("INFO", tag, msg) - private fun assertWarn(tag: String = defaultTag, msg: String = warn) = w.assertLogged("WARN", tag, msg) - private fun assertError(tag: String = defaultTag, msg: String = error) = e.assertLogged("ERROR", tag, msg) - private fun String?.assertLogged(level: String, tag: String, msg: String) = - assertEquals("$level: $tag: $msg", this) + private fun List.assertLogged(level: String, tag: String, msg: String) = + assertTrue(this.contains("$level: $tag: $msg")) } diff --git a/android/player/src/test/java/com/intuit/player/android/renderer/BaseRenderableAssetTest.kt b/android/player/src/test/java/com/intuit/player/android/renderer/BaseRenderableAssetTest.kt index f7694c8af..69b0bb937 100644 --- a/android/player/src/test/java/com/intuit/player/android/renderer/BaseRenderableAssetTest.kt +++ b/android/player/src/test/java/com/intuit/player/android/renderer/BaseRenderableAssetTest.kt @@ -5,25 +5,31 @@ import android.widget.TextView import com.intuit.player.android.AndroidPlayer import com.intuit.player.android.AssetContext import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.extensions.CoroutineTestDispatcherExtension import com.intuit.player.android.utils.TestAssetsPlugin import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.flow.Flow +import com.intuit.player.jvm.core.player.state.InProgressState import com.intuit.player.jvm.core.plugins.Plugin import com.intuit.player.plugins.beacon.BeaconPlugin +import io.mockk.coEvery import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.junit5.MockKExtension +import io.mockk.mockk import io.mockk.spyk import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(MockKExtension::class) +@ExtendWith(CoroutineTestDispatcherExtension::class) internal abstract class BaseRenderableAssetTest { data class BeaconArgs( val action: String, val element: String, val asset: Asset, - val data: Any? = null + val data: Any? = null, ) var beaconPlugin: BeaconPlugin = spyk(BeaconPlugin()) @@ -39,7 +45,7 @@ internal abstract class BaseRenderableAssetTest { lastBeaconed = BeaconArgs(action as String, element as String, asset as Asset, data) } - every { mockRenderableAsset.render(any()) } returns TextView(mockContext) + coEvery { mockRenderableAsset.render(any()) } returns TextView(mockContext) } @MockK @@ -53,7 +59,11 @@ internal abstract class BaseRenderableAssetTest { } open val player by lazy { - AndroidPlayer(plugins) + AndroidPlayer(plugins).apply { + val inProgressState: InProgressState = mockk() + every { inProgressState.flow } returns Flow(id = "fake-flow-test") + hooks.state.call(HashMap(), arrayOf(inProgressState)) + } } open val assetContext by lazy { diff --git a/android/player/src/test/java/com/intuit/player/android/renderer/BrokenAssetTest.kt b/android/player/src/test/java/com/intuit/player/android/renderer/BrokenAssetTest.kt new file mode 100644 index 000000000..72b62f3b8 --- /dev/null +++ b/android/player/src/test/java/com/intuit/player/android/renderer/BrokenAssetTest.kt @@ -0,0 +1,77 @@ +package com.intuit.player.android.renderer + +import android.content.Context +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.intuit.player.android.AndroidPlayer +import com.intuit.player.android.AssetContext +import com.intuit.player.android.asset.StaleViewException +import com.intuit.player.android.utils.BrokenAsset +import com.intuit.player.android.utils.BrokenAsset.Companion.asset +import com.intuit.player.android.utils.TestAssetsPlugin +import com.intuit.player.jvm.core.player.PlayerException +import com.intuit.player.jvm.core.player.state.ErrorState +import com.intuit.player.jvm.utils.start +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.extension.ExtendWith + +@ExtendWith(MockKExtension::class) +internal class BrokenAssetTest { + + private val runtime = BrokenAsset.runtime + + @MockK + lateinit var mockContext: Context + + val player: AndroidPlayer by lazy { + AndroidPlayer(TestAssetsPlugin) + } + + val baseContext by lazy { + AssetContext(null, runtime.asset(), player, ::BrokenAsset) + } + + @BeforeEach fun setup() { + player.start(BrokenAsset.sampleFlow) + } + + @Test + fun `invalidate view should fail on first render`() { + assertThrows { + BrokenAsset(baseContext.copy(asset = runtime.asset(shouldFail = true))).render(mockContext) + } + } + + @Test + fun `invalidate view should handle gracefully in a rehydrate (if asset renders properly the second time)`() { + assertTrue(BrokenAsset(baseContext.copy(asset = runtime.asset(layout = BrokenAsset.Layout.Frame))).render(mockContext) is FrameLayout) + assertTrue(BrokenAsset(baseContext.copy(asset = runtime.asset(layout = BrokenAsset.Layout.Linear))).render(mockContext) is LinearLayout) + } + + @Test + fun `manual rehydration should fail the player on invalidate view`() { + BrokenAsset(baseContext.copy(asset = runtime.asset(layout = BrokenAsset.Layout.Frame))).apply { + assertTrue(render(mockContext) is FrameLayout) + data.layout = BrokenAsset.Layout.Linear + rehydrate() + } + assertTrue(player.state is ErrorState) + } + + @Test + fun `cannot render without a context`() { + assertEquals( + "Android context not found! Ensure the asset is rendered with a valid Android context.", + assertThrows { + BrokenAsset(baseContext).requireContext() + }.message, + ) + assertTrue(player.state is ErrorState) + } +} diff --git a/android/player/src/test/java/com/intuit/player/android/renderer/HydrationScopeTest.kt b/android/player/src/test/java/com/intuit/player/android/renderer/HydrationScopeTest.kt index 49f80bea3..83d755461 100644 --- a/android/player/src/test/java/com/intuit/player/android/renderer/HydrationScopeTest.kt +++ b/android/player/src/test/java/com/intuit/player/android/renderer/HydrationScopeTest.kt @@ -4,8 +4,9 @@ import android.view.View import android.widget.TextView import com.intuit.player.android.AndroidPlayer import com.intuit.player.android.AssetContext -import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.asset.SuspendableAsset import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.runtime.serialize import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializer import com.intuit.player.jvm.core.flow.forceTransition @@ -15,11 +16,16 @@ import com.intuit.player.jvm.utils.makeFlow import com.intuit.player.jvm.utils.start import com.intuit.player.jvm.utils.test.runBlockingTest import com.intuit.player.plugins.coroutines.flowScope +import kotlinx.coroutines.cancel import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import kotlinx.serialization.json.Json.Default.encodeToString -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -27,11 +33,10 @@ internal class HydrationScopeTest : BaseRenderableAssetTest() { private var completed: Boolean = false - @Suppress("DEPRECATION_ERROR") - inner class TestAsset(assetContext: AssetContext) : RenderableAsset(assetContext) { - override fun initView(): View = TextView(context) + inner class TestAsset(assetContext: AssetContext) : SuspendableAsset(assetContext, NodeSerializer()) { + override suspend fun initView(data: Node): View = TextView(context) - override fun View.hydrate() { + override suspend fun View.hydrate(data: Node) { hydrationScope.launch { delay(500) completed = true @@ -45,7 +50,7 @@ internal class HydrationScopeTest : BaseRenderableAssetTest() { mapOf( "id" to "some-id", "type" to "test", - ) + ), ) as Asset override val player by lazy { @@ -64,14 +69,23 @@ internal class HydrationScopeTest : BaseRenderableAssetTest() { } @Test - fun `test hydration scope can launch coroutines`() { + fun `test awaiting async view stub doesn't cancel parent scope`() = runBlocking { val test = TestAsset(assetContext) - test.render(mockContext) + val asyncView = test.render(mockContext) as SuspendableAsset.AsyncViewStub + test.currentHydrationScope.cancel("hello") + assertNull(asyncView.awaitView()) + } + + @Test + fun `test hydration scope can launch coroutines`() = runBlocking { + val test = TestAsset(assetContext) + val asyncView = test.render(mockContext) as SuspendableAsset.AsyncViewStub + asyncView.awaitView() waitForCompleted() } @Test - fun `test existing hydration scope is cancelled on re-render`() { + fun `test existing hydration scope is cancelled on re-render`() = runBlocking { val test = TestAsset(assetContext) test.render(mockContext) val currentHydrationScope = test.currentHydrationScope @@ -82,7 +96,7 @@ internal class HydrationScopeTest : BaseRenderableAssetTest() { } @Test - fun `test hydration scope is refreshed on re-render`() { + fun `test hydration scope is refreshed on re-render`() = runBlocking { val test = TestAsset(assetContext) test.render(mockContext) val firstHydrationScope = test.currentHydrationScope @@ -96,7 +110,7 @@ internal class HydrationScopeTest : BaseRenderableAssetTest() { } @Test - fun `test hydration scope is cancelled on flow end`() { + fun `test hydration scope is cancelled on flow end`() = runBlocking { val test = TestAsset(assetContext) test.render(mockContext) player.inProgressState?.forceTransition("") @@ -105,6 +119,16 @@ internal class HydrationScopeTest : BaseRenderableAssetTest() { assertFalse(completed) } + @Test + fun `test hydration scope is cancelled on player release`() = runBlocking { + val test = TestAsset(assetContext) + test.render(mockContext) + player.release() + assertFalse(player.flowScope!!.isActive) + assertFalse(test.currentHydrationScope.isActive) + assertFalse(completed) + } + private fun waitForCompleted(count: Int = 5, delay: Long = 500) { waitForCondition(count, delay) { completed } } diff --git a/android/player/src/test/java/com/intuit/player/android/renderer/NestedAssetTest.kt b/android/player/src/test/java/com/intuit/player/android/renderer/NestedAssetTest.kt index 7d501e279..0cd8257d1 100644 --- a/android/player/src/test/java/com/intuit/player/android/renderer/NestedAssetTest.kt +++ b/android/player/src/test/java/com/intuit/player/android/renderer/NestedAssetTest.kt @@ -3,10 +3,16 @@ package com.intuit.player.android.renderer import android.widget.LinearLayout import com.intuit.player.android.AndroidPlayer import com.intuit.player.android.AssetContext +import com.intuit.player.android.asset.SuspendableAsset import com.intuit.player.android.utils.NestedAsset import com.intuit.player.android.utils.SimpleAsset import com.intuit.player.android.utils.awaitFirstView +import com.intuit.player.jvm.core.flow.Flow +import com.intuit.player.jvm.core.player.state.InProgressState import com.intuit.player.jvm.utils.test.runBlockingTest +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -18,6 +24,9 @@ internal class NestedAssetTest : BaseRenderableAssetTest() { override val player get() = AndroidPlayer(beaconPlugin).apply { registerAsset("simple", ::SimpleAsset) registerAsset("nested", ::NestedAsset) + val inProgressState = mockk() + every { inProgressState.flow } returns Flow() + hooks.state.call(HashMap(), arrayOf(inProgressState)) } override val assetContext: AssetContext by lazy { @@ -25,15 +34,19 @@ internal class NestedAssetTest : BaseRenderableAssetTest() { } @Test - fun `tested nested asset constructs`() { - val nested = NestedAsset(assetContext).render(mockContext) + fun `tested nested asset constructs`() = runBlocking { + val nested = NestedAsset(assetContext).render(mockContext).let { + if (it is SuspendableAsset.AsyncViewStub) it.awaitView() else it + } assertTrue(nested is LinearLayout) } @Test fun `test nested asset context`() = runBlockingTest { val asset = player.awaitFirstView(NestedAsset.sampleFlow)!! as NestedAsset - asset.render(mockContext) + asset.render(mockContext).let { + if (it is SuspendableAsset.AsyncViewStub) it.awaitView() else it + } assertEquals(mockContext, NestedAsset.dummy?.context) NestedAsset.dummy2?.forEach { assertEquals(mockContext, it?.context) diff --git a/android/player/src/test/java/com/intuit/player/android/renderer/SimpleAssetTest.kt b/android/player/src/test/java/com/intuit/player/android/renderer/SimpleAssetTest.kt index a36a4ba9f..e303fb2e3 100644 --- a/android/player/src/test/java/com/intuit/player/android/renderer/SimpleAssetTest.kt +++ b/android/player/src/test/java/com/intuit/player/android/renderer/SimpleAssetTest.kt @@ -2,11 +2,14 @@ package com.intuit.player.android.renderer import android.widget.TextView import com.intuit.player.android.AssetContext +import com.intuit.player.android.R import com.intuit.player.android.utils.SimpleAsset import com.intuit.player.android.utils.SimpleAsset.Companion.sampleFlow import com.intuit.player.android.utils.stringify import com.intuit.player.android.withContext -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test internal class SimpleAssetTest : BaseRenderableAssetTest() { @@ -23,6 +26,38 @@ internal class SimpleAssetTest : BaseRenderableAssetTest() { assertTrue(simple is TextView) } + @Test + fun `test rendering with some styles`() { + val simple = SimpleAsset(assetContext.withContext(mockContext)).run { + render(R.style.TextAppearance_AppCompat) + } + assertTrue(simple is TextView) + } + + @Test + fun `test rendering with some styles using another render method`() { + val simple = SimpleAsset(assetContext.withContext(mockContext)).run { + render(listOf(R.style.TextAppearance_AppCompat)) + } + assertTrue(simple is TextView) + } + + @Test + fun `test rendering with some styles and a tag`() { + val simple = SimpleAsset(assetContext.withContext(mockContext)).run { + render(R.style.TextAppearance_AppCompat, tag = "tag") + } + assertTrue(simple is TextView) + } + + @Test + fun `test rendering with some styles and a tag using another render method`() { + val simple = SimpleAsset(assetContext.withContext(mockContext)).run { + render(listOf(R.style.TextAppearance_AppCompat), "tag") + } + assertTrue(simple is TextView) + } + @Test fun `test rendering with tag`() { player.start(sampleFlow.stringify()) diff --git a/android/player/src/test/java/com/intuit/player/android/utils/BrokenAsset.kt b/android/player/src/test/java/com/intuit/player/android/utils/BrokenAsset.kt new file mode 100644 index 000000000..10efdd500 --- /dev/null +++ b/android/player/src/test/java/com/intuit/player/android/utils/BrokenAsset.kt @@ -0,0 +1,61 @@ +package com.intuit.player.android.utils + +import android.view.View +import android.widget.FrameLayout +import android.widget.LinearLayout +import com.eclipsesource.v8.debug.mirror.Frame +import com.intuit.player.android.AssetContext +import com.intuit.player.android.asset.DecodableAsset +import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.serialize +import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer +import com.intuit.player.jvm.j2v8.bridge.runtime.J2V8 +import com.intuit.player.jvm.utils.makeFlow +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json + +internal class BrokenAsset(assetContext: AssetContext) : DecodableAsset(assetContext, Data.serializer()) { + + @Serializable + data class Data( + var layout: Layout, + val shouldFail: Boolean, + ) + + @Serializable + enum class Layout { + Frame, Linear + } + + override fun initView() = when (data.layout) { + Layout.Frame -> FrameLayout(requireContext()) + Layout.Linear -> LinearLayout(requireContext()) + } + + override fun View.hydrate() { + if (data.shouldFail || ( + data.layout == Layout.Frame && this is LinearLayout + ) || ( + data.layout == Layout.Linear && this is FrameLayout + ) + ) { + invalidateView() + } + } + + companion object { + val sampleMap = mapOf( + "id" to "broken-asset", + "type" to "broken", + "layout" to "Frame", + "shouldFail" to false, + ) + fun Runtime<*>.asset(layout: Layout = Layout.Frame, shouldFail: Boolean = false): Asset = + serialize(sampleMap + mapOf("layout" to layout.toString(), "shouldFail" to shouldFail)) as Asset + val runtime = J2V8.create() + val sampleAsset = runtime.serialize(sampleMap) as Asset + val sampleJson = Json.encodeToJsonElement(GenericSerializer(), sampleMap) + val sampleFlow = makeFlow(sampleJson) + } +} diff --git a/android/player/src/test/java/com/intuit/player/android/utils/json.kt b/android/player/src/test/java/com/intuit/player/android/utils/Json.kt similarity index 100% rename from android/player/src/test/java/com/intuit/player/android/utils/json.kt rename to android/player/src/test/java/com/intuit/player/android/utils/Json.kt diff --git a/android/player/src/test/java/com/intuit/player/android/utils/NestedAsset.kt b/android/player/src/test/java/com/intuit/player/android/utils/NestedAsset.kt index 9b49995cf..edc420bf9 100644 --- a/android/player/src/test/java/com/intuit/player/android/utils/NestedAsset.kt +++ b/android/player/src/test/java/com/intuit/player/android/utils/NestedAsset.kt @@ -4,23 +4,26 @@ import android.view.View import android.widget.LinearLayout import com.intuit.player.android.AssetContext import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.asset.SuspendableAsset import com.intuit.player.android.extensions.into import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.runtime.serialize import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializer import com.intuit.player.jvm.j2v8.bridge.runtime.J2V8 import com.intuit.player.jvm.utils.makeFlow import kotlinx.serialization.json.Json @Suppress("DEPRECATION_ERROR") -internal class NestedAsset(assetContext: AssetContext) : RenderableAsset(assetContext) { +internal class NestedAsset(assetContext: AssetContext) : SuspendableAsset(assetContext, NodeSerializer()) { val nested = expand("nested") val nestedList = expandList("nestedAssets") - override fun initView() = LinearLayout(context) + override suspend fun initView(data: Node) = LinearLayout(context) - override fun View.hydrate() { + override suspend fun View.hydrate(data: Node) { require(this is LinearLayout) nested?.render() into this dummy = nested @@ -39,18 +42,18 @@ internal class NestedAsset(assetContext: AssetContext) : RenderableAsset(assetCo "asset" to mapOf( "id" to "some-nested-id", "type" to "simple", - "metaData" to mapOf("a" to "b") - ) + "metaData" to mapOf("a" to "b"), + ), ), "nestedAssets" to listOf( mapOf( "asset" to mapOf( "id" to "some-nested-id", "type" to "simple", - "metaData" to mapOf("a" to "b") - ) - ) - ) + "metaData" to mapOf("a" to "b"), + ), + ), + ), ) val sampleAsset: Asset = J2V8.create().serialize(sampleMap) as Asset val sampleJson = Json.encodeToJsonElement(GenericSerializer(), sampleMap) diff --git a/android/player/src/test/java/com/intuit/player/android/utils/OtherSimpleAsset.kt b/android/player/src/test/java/com/intuit/player/android/utils/OtherSimpleAsset.kt index 9ce122875..5ba0a525f 100644 --- a/android/player/src/test/java/com/intuit/player/android/utils/OtherSimpleAsset.kt +++ b/android/player/src/test/java/com/intuit/player/android/utils/OtherSimpleAsset.kt @@ -3,26 +3,28 @@ package com.intuit.player.android.utils import android.view.View import android.widget.ImageView import com.intuit.player.android.AssetContext -import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.asset.SuspendableAsset import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.runtime.serialize import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializer import com.intuit.player.jvm.j2v8.bridge.runtime.J2V8 import com.intuit.player.jvm.utils.makeFlow import kotlinx.serialization.json.Json @Suppress("DEPRECATION_ERROR") -internal class OtherSimpleAsset(assetContext: AssetContext) : RenderableAsset(assetContext) { +internal class OtherSimpleAsset(assetContext: AssetContext) : SuspendableAsset(assetContext, NodeSerializer()) { - override fun initView() = ImageView(context) + override suspend fun initView(data: Node) = ImageView(context) - override fun View.hydrate() = Unit + override suspend fun View.hydrate(data: Node) = Unit companion object { val sampleMap = mapOf( "id" to "some-id", "type" to "simple", - "metaData" to mapOf("role" to "other") + "metaData" to mapOf("role" to "other"), ) val sampleAsset: Asset = J2V8.create().serialize(sampleMap) as Asset val sampleJson = Json.encodeToJsonElement(GenericSerializer(), sampleMap) diff --git a/android/player/src/test/java/com/intuit/player/android/utils/SimpleAsset.kt b/android/player/src/test/java/com/intuit/player/android/utils/SimpleAsset.kt index 1dab8b870..89e86f97e 100644 --- a/android/player/src/test/java/com/intuit/player/android/utils/SimpleAsset.kt +++ b/android/player/src/test/java/com/intuit/player/android/utils/SimpleAsset.kt @@ -3,8 +3,9 @@ package com.intuit.player.android.utils import android.view.View import android.widget.TextView import com.intuit.player.android.AssetContext -import com.intuit.player.android.asset.RenderableAsset +import com.intuit.player.android.asset.DecodableAsset import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.runtime.serialize import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.j2v8.bridge.runtime.J2V8 @@ -12,7 +13,7 @@ import com.intuit.player.jvm.utils.makeFlow import kotlinx.serialization.json.Json @Suppress("DEPRECATION_ERROR") -internal class SimpleAsset(assetContext: AssetContext) : RenderableAsset(assetContext) { +internal class SimpleAsset(assetContext: AssetContext) : DecodableAsset(assetContext, Node.serializer()) { override fun initView() = TextView(context) @@ -23,7 +24,7 @@ internal class SimpleAsset(assetContext: AssetContext) : RenderableAsset(assetCo "id" to "simple-asset", "data" to "{{someBinding}}", "type" to "simple", - "metaData" to mapOf("a" to "b") + "metaData" to mapOf("a" to "b"), ) val runtime = J2V8.create() val sampleAsset = runtime.serialize(sampleMap) as Asset diff --git a/android/player/src/test/java/com/intuit/player/android/utils/TestAssetsPlugin.kt b/android/player/src/test/java/com/intuit/player/android/utils/TestAssetsPlugin.kt index 4b24cd180..02ad12175 100644 --- a/android/player/src/test/java/com/intuit/player/android/utils/TestAssetsPlugin.kt +++ b/android/player/src/test/java/com/intuit/player/android/utils/TestAssetsPlugin.kt @@ -11,9 +11,10 @@ internal object TestAssetsPlugin : AndroidPlayerPlugin { androidPlayer.registerAsset( mapOf( TYPE to "simple", - METADATA to mapOf("role" to "other") + METADATA to mapOf("role" to "other"), ), - ::OtherSimpleAsset + ::OtherSimpleAsset, ) + androidPlayer.registerAsset("broken", ::BrokenAsset) } } diff --git a/android/player/src/test/java/com/intuit/player/android/utils/updates.kt b/android/player/src/test/java/com/intuit/player/android/utils/Updates.kt similarity index 84% rename from android/player/src/test/java/com/intuit/player/android/utils/updates.kt rename to android/player/src/test/java/com/intuit/player/android/utils/Updates.kt index 0c8c9538c..841684c8f 100644 --- a/android/player/src/test/java/com/intuit/player/android/utils/updates.kt +++ b/android/player/src/test/java/com/intuit/player/android/utils/Updates.kt @@ -29,18 +29,21 @@ internal fun AndroidPlayer.updates(flow: JsonElement, take: Int = 1) = updates(f internal fun AndroidPlayer.updates(flow: String, take: Int = 1): Flow = callbackFlow { var count = 0 onUpdate { asset, _ -> - offer(Update.Asset(asset, count)) + trySend(Update.Asset(asset, count)).isSuccess if (++count >= take) close() } hooks.state.tap { state -> - offer(Update.State(state ?: ErrorState.from("state was null"))) + trySend(Update.State(state ?: ErrorState.from("state was null"))).isSuccess } start(flow).onComplete { close( - it.exceptionOrNull() ?: if (count != take) - PlayerException("did not meet expected asset updates ($count != $take)") else null + it.exceptionOrNull() ?: if (count != take) { + PlayerException("did not meet expected asset updates ($count != $take)") + } else { + null + }, ) } diff --git a/jvm/BUILD b/jvm/BUILD index 1b6024ac7..074eb1362 100644 --- a/jvm/BUILD +++ b/jvm/BUILD @@ -1,4 +1,4 @@ -load("@io_bazel_rules_kotlin//kotlin:core.bzl", "kt_compiler_plugin", "kt_kotlinc_options") +load("@io_bazel_rules_kotlin//kotlin:core.bzl", "define_kt_toolchain", "kt_compiler_plugin", "kt_kotlinc_options") load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library") load("@io_bazel_rules_kotlin//kotlin:lint.bzl", "ktlint_config") load("//jvm/dependencies:versions.bzl", "versions") @@ -49,6 +49,10 @@ kt_kotlinc_options( ktlint_config( name = "lint_config", - editorconfig = "//:.editorconfig", visibility = ["//visibility:public"], ) + +define_kt_toolchain( + name = "kotlin_toolchain", + kotlinc_options = ":test_options", +) diff --git a/jvm/core/api/core.api b/jvm/core/api/core.api index 8d4a2ce77..fe294c0ff 100644 --- a/jvm/core/api/core.api +++ b/jvm/core/api/core.api @@ -1,1584 +1,1596 @@ public class com/intuit/player/jvm/core/asset/Asset : com/intuit/player/jvm/core/bridge/Node, com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/asset/Asset$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public fun clear ()V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/lang/String; - public final fun component3 ()Lcom/intuit/player/jvm/core/asset/MetaData; - public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; - public fun computeIfAbsent (Ljava/lang/String;Ljava/util/function/Function;)Ljava/lang/Object; - public synthetic fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun computeIfPresent (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public final fun containsKey (Ljava/lang/Object;)Z - public fun containsKey (Ljava/lang/String;)Z - public fun containsValue (Ljava/lang/Object;)Z - public fun deserialize (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public final fun entrySet ()Ljava/util/Set; - public fun equals (Ljava/lang/Object;)Z - public final fun get (Ljava/lang/Object;)Ljava/lang/Object; - public fun get (Ljava/lang/String;)Ljava/lang/Object; - public final fun getAsset (Ljava/lang/String;)Lcom/intuit/player/jvm/core/asset/AssetWrapper; - public fun getBoolean (Ljava/lang/String;)Ljava/lang/Boolean; - public fun getDouble (Ljava/lang/String;)Ljava/lang/Double; - public fun getEntries ()Ljava/util/Set; - public fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; - public fun getFunction (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; - public final fun getId ()Ljava/lang/String; - public fun getInt (Ljava/lang/String;)Ljava/lang/Integer; - public fun getKeys ()Ljava/util/Set; - public fun getList (Ljava/lang/String;)Ljava/util/List; - public fun getLong (Ljava/lang/String;)Ljava/lang/Long; - public final fun getMetaData ()Lcom/intuit/player/jvm/core/asset/MetaData; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getObject (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; - public fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; - public fun getSerializable (Ljava/lang/String;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public fun getSize ()I - public fun getString (Ljava/lang/String;)Ljava/lang/String; - public final fun getType ()Ljava/lang/String; - public fun getValues ()Ljava/util/Collection; - public fun hashCode ()I - public fun isEmpty ()Z - public fun isReleased ()Z - public fun isUndefined ()Z - public final fun keySet ()Ljava/util/Set; - public synthetic fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun merge (Ljava/lang/String;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun nativeReferenceEquals (Ljava/lang/Object;)Z - public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun putAll (Ljava/util/Map;)V - public synthetic fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun putIfAbsent (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z - public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z - public fun replace (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun replace (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Z - public fun replaceAll (Ljava/util/function/BiFunction;)V - public final fun size ()I - public fun toString ()Ljava/lang/String; - public final fun values ()Ljava/util/Collection; +public static final field Companion Lcom/intuit/player/jvm/core/asset/Asset$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public fun clear ()V +public final fun component1 ()Ljava/lang/String; +public final fun component2 ()Ljava/lang/String; +public final fun component3 ()Lcom/intuit/player/jvm/core/asset/MetaData; +public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; +public fun computeIfAbsent (Ljava/lang/String;Ljava/util/function/Function;)Ljava/lang/Object; +public synthetic fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun computeIfPresent (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public final fun containsKey (Ljava/lang/Object;)Z +public fun containsKey (Ljava/lang/String;)Z +public fun containsValue (Ljava/lang/Object;)Z +public fun deserialize (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public final fun entrySet ()Ljava/util/Set; +public fun equals (Ljava/lang/Object;)Z +public final fun get (Ljava/lang/Object;)Ljava/lang/Object; +public fun get (Ljava/lang/String;)Ljava/lang/Object; +public final fun getAsset (Ljava/lang/String;)Lcom/intuit/player/jvm/core/asset/AssetWrapper; +public fun getBoolean (Ljava/lang/String;)Ljava/lang/Boolean; +public fun getDouble (Ljava/lang/String;)Ljava/lang/Double; +public fun getEntries ()Ljava/util/Set; +public fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; +public fun getFunction (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; +public final fun getId ()Ljava/lang/String; +public fun getInt (Ljava/lang/String;)Ljava/lang/Integer; +public fun getKeys ()Ljava/util/Set; +public fun getList (Ljava/lang/String;)Ljava/util/List; +public fun getLong (Ljava/lang/String;)Ljava/lang/Long; +public final fun getMetaData ()Lcom/intuit/player/jvm/core/asset/MetaData; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getObject (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; +public fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public fun getSerializable (Ljava/lang/String;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public fun getSize ()I +public fun getString (Ljava/lang/String;)Ljava/lang/String; +public final fun getType ()Ljava/lang/String; +public fun getValues ()Ljava/util/Collection; +public fun hashCode ()I +public fun isEmpty ()Z +public fun isReleased ()Z +public fun isUndefined ()Z +public final fun keySet ()Ljava/util/Set; +public synthetic fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun merge (Ljava/lang/String;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun nativeReferenceEquals (Ljava/lang/Object;)Z +public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; +public fun putAll (Ljava/util/Map;)V +public synthetic fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public fun putIfAbsent (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; +public fun remove (Ljava/lang/Object;)Ljava/lang/Object; +public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z +public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z +public fun replace (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; +public fun replace (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Z +public fun replaceAll (Ljava/util/function/BiFunction;)V +public final fun size ()I +public fun toString ()Ljava/lang/String; +public final fun values ()Ljava/util/Collection; } public final class com/intuit/player/jvm/core/asset/Asset$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/asset/AssetKt { - public static final fun getValue (Lcom/intuit/player/jvm/core/asset/Asset;)Ljava/lang/String; +public static final fun getValue (Lcom/intuit/player/jvm/core/asset/Asset;)Ljava/lang/String; } public final class com/intuit/player/jvm/core/asset/AssetWrapper : com/intuit/player/jvm/core/bridge/NodeWrapper { - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun getAsset ()Lcom/intuit/player/jvm/core/asset/Asset; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun getAsset ()Lcom/intuit/player/jvm/core/asset/Asset; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public class com/intuit/player/jvm/core/asset/MetaData : com/intuit/player/jvm/core/bridge/NodeWrapper { - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun getBeacon ()Lkotlinx/serialization/json/JsonElement; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getRef ()Ljava/lang/String; - public final fun getRole ()Ljava/lang/String; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun getBeacon ()Lkotlinx/serialization/json/JsonElement; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getRef ()Ljava/lang/String; +public final fun getRole ()Ljava/lang/String; } public abstract interface class com/intuit/player/jvm/core/bridge/Completable { - public abstract fun asFlow (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun onComplete (Lkotlin/jvm/functions/Function1;)V +public abstract fun asFlow (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +public abstract fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +public abstract fun onComplete (Lkotlin/jvm/functions/Function1;)V } public final class com/intuit/player/jvm/core/bridge/Completed : com/intuit/player/jvm/core/bridge/Completable { - public fun (Ljava/lang/Object;)V - public fun asFlow (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun getValue ()Ljava/lang/Object; - public fun onComplete (Lkotlin/jvm/functions/Function1;)V +public fun (Ljava/lang/Object;)V +public fun asFlow (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +public fun await (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +public final fun getValue ()Ljava/lang/Object; +public fun onComplete (Lkotlin/jvm/functions/Function1;)V } public abstract interface class com/intuit/player/jvm/core/bridge/Invokable : kotlin/Function { - public abstract fun invoke ([Ljava/lang/Object;)Ljava/lang/Object; +public abstract fun invoke ([Ljava/lang/Object;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/bridge/InvokableKt { - public static final fun invokeVararg (Lkotlin/Function;[Ljava/lang/Object;)Ljava/lang/Object; - public static final fun toFunction (Lcom/intuit/player/jvm/core/bridge/Invokable;Ljava/lang/String;)Lkotlin/Function; +public static final fun invokeVararg (Lkotlin/Function;[Ljava/lang/Object;)Ljava/lang/Object; +public static final fun toFunction (Lcom/intuit/player/jvm/core/bridge/Invokable;Ljava/lang/String;)Lkotlin/Function; } public final class com/intuit/player/jvm/core/bridge/JSErrorException : com/intuit/player/jvm/core/player/PlayerException, com/intuit/player/jvm/core/bridge/NodeWrapper { - public fun (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/Throwable;)V - public synthetic fun (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun getMessage ()Ljava/lang/String; - public final fun getName ()Ljava/lang/String; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/Throwable;)V +public synthetic fun (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public fun getMessage ()Ljava/lang/String; +public final fun getName ()Ljava/lang/String; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public abstract interface class com/intuit/player/jvm/core/bridge/Node : java/util/Map, kotlin/jvm/internal/markers/KMappedMarker { - public abstract fun deserialize (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public abstract fun getBoolean (Ljava/lang/String;)Ljava/lang/Boolean; - public abstract fun getDouble (Ljava/lang/String;)Ljava/lang/Double; - public abstract fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; - public abstract fun getFunction (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; - public abstract fun getInt (Ljava/lang/String;)Ljava/lang/Integer; - public abstract fun getList (Ljava/lang/String;)Ljava/util/List; - public abstract fun getLong (Ljava/lang/String;)Ljava/lang/Long; - public abstract fun getObject (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; - public abstract fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; - public abstract fun getSerializable (Ljava/lang/String;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public abstract fun getString (Ljava/lang/String;)Ljava/lang/String; - public abstract fun isReleased ()Z - public abstract fun isUndefined ()Z - public abstract fun nativeReferenceEquals (Ljava/lang/Object;)Z +public abstract fun deserialize (Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public abstract fun getBoolean (Ljava/lang/String;)Ljava/lang/Boolean; +public abstract fun getDouble (Ljava/lang/String;)Ljava/lang/Double; +public abstract fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; +public abstract fun getFunction (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; +public abstract fun getInt (Ljava/lang/String;)Ljava/lang/Integer; +public abstract fun getList (Ljava/lang/String;)Ljava/util/List; +public abstract fun getLong (Ljava/lang/String;)Ljava/lang/Long; +public abstract fun getObject (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; +public abstract fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public abstract fun getSerializable (Ljava/lang/String;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public abstract fun getString (Ljava/lang/String;)Ljava/lang/String; +public abstract fun isReleased ()Z +public abstract fun isUndefined ()Z +public abstract fun nativeReferenceEquals (Ljava/lang/Object;)Z } public final class com/intuit/player/jvm/core/bridge/Node$DefaultImpls { - public static fun getBoolean (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Boolean; - public static fun getDouble (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Double; - public static fun getFormat (Lcom/intuit/player/jvm/core/bridge/Node;)Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; - public static fun getFunction (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; - public static fun getInt (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Integer; - public static fun getList (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/util/List; - public static fun getLong (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Long; - public static fun getObject (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; - public static fun getString (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/String; +public static fun getBoolean (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Boolean; +public static fun getDouble (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Double; +public static fun getFormat (Lcom/intuit/player/jvm/core/bridge/Node;)Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; +public static fun getFunction (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; +public static fun getInt (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Integer; +public static fun getList (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/util/List; +public static fun getLong (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/Long; +public static fun getObject (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; +public static fun getString (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Ljava/lang/String; } public final class com/intuit/player/jvm/core/bridge/NodeKt { - public static final fun getFormat (Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; - public static final fun getJson (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Lkotlinx/serialization/json/JsonElement; - public static final fun getRuntime (Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; - public static final fun toJson (Lcom/intuit/player/jvm/core/bridge/Node;)Lkotlinx/serialization/json/JsonElement; +public static final fun getFormat (Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; +public static final fun getJson (Lcom/intuit/player/jvm/core/bridge/Node;Ljava/lang/String;)Lkotlinx/serialization/json/JsonElement; +public static final fun getRuntime (Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public static final fun toJson (Lcom/intuit/player/jvm/core/bridge/Node;)Lkotlinx/serialization/json/JsonElement; } public abstract interface class com/intuit/player/jvm/core/bridge/NodeWrapper { - public abstract fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public abstract fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/bridge/PlayerRuntimeException : com/intuit/player/jvm/core/player/PlayerException { - public fun (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;)V - public synthetic fun (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public fun (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;)V +public synthetic fun (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; } public final class com/intuit/player/jvm/core/bridge/PlayerRuntimeExceptionKt { - public static final fun PlayerRuntimeException (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;)Lcom/intuit/player/jvm/core/bridge/PlayerRuntimeException; - public static synthetic fun PlayerRuntimeException$default (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/PlayerRuntimeException; +public static final fun PlayerRuntimeException (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;)Lcom/intuit/player/jvm/core/bridge/PlayerRuntimeException; +public static synthetic fun PlayerRuntimeException$default (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;Ljava/lang/Throwable;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/PlayerRuntimeException; } public abstract interface class com/intuit/player/jvm/core/bridge/Promise$Api { - public abstract fun reject ([Ljava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Promise; - public abstract fun resolve ([Ljava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Promise; +public abstract fun reject ([Ljava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Promise; +public abstract fun resolve ([Ljava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Promise; } public final class com/intuit/player/jvm/core/bridge/Promise$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/Promise$Serializer : kotlinx/serialization/KSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/bridge/Promise$Serializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/Promise; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/Promise;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public static final field INSTANCE Lcom/intuit/player/jvm/core/bridge/Promise$Serializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/Promise; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/Promise;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } public final class com/intuit/player/jvm/core/bridge/PromiseException : com/intuit/player/jvm/core/player/PlayerException { - public fun (Ljava/lang/String;Ljava/lang/Throwable;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public fun (Ljava/lang/String;Ljava/lang/Throwable;)V +public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class com/intuit/player/jvm/core/bridge/PromiseKt { - public static final fun Promise (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Lkotlin/jvm/functions/Function2;)Lcom/intuit/player/jvm/core/bridge/Promise; - public static final fun getPromise (Lcom/intuit/player/jvm/core/bridge/Node;)Lcom/intuit/player/jvm/core/bridge/Promise$Api; - public static final fun getPromise (Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)Lcom/intuit/player/jvm/core/bridge/Promise$Api; - public static final fun getPromise (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)Lcom/intuit/player/jvm/core/bridge/Promise$Api; +public static final fun Promise (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Lkotlin/jvm/functions/Function2;)Lcom/intuit/player/jvm/core/bridge/Promise; +public static final fun getPromise (Lcom/intuit/player/jvm/core/bridge/Node;)Lcom/intuit/player/jvm/core/bridge/Promise$Api; +public static final fun getPromise (Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)Lcom/intuit/player/jvm/core/bridge/Promise$Api; +public static final fun getPromise (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)Lcom/intuit/player/jvm/core/bridge/Promise$Api; } public final class com/intuit/player/jvm/core/bridge/global/JSIterator : com/intuit/player/jvm/core/bridge/NodeWrapper, java/util/Iterator, kotlin/jvm/internal/markers/KMappedMarker { - public static final field Companion Lcom/intuit/player/jvm/core/bridge/global/JSIterator$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;)V - public final fun getItemSerializer ()Lkotlinx/serialization/KSerializer; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun hasNext ()Z - public fun next ()Ljava/lang/Object; - public fun remove ()V +public static final field Companion Lcom/intuit/player/jvm/core/bridge/global/JSIterator$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;)V +public final fun getItemSerializer ()Lkotlinx/serialization/KSerializer; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun hasNext ()Z +public fun next ()Ljava/lang/Object; +public fun remove ()V } public final class com/intuit/player/jvm/core/bridge/global/JSIterator$Companion { - public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; +public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/global/JSIterator$Serializer : com/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer { - public fun (Lkotlinx/serialization/KSerializer;)V - public final fun getItemSerializer ()Lkotlinx/serialization/KSerializer; +public fun (Lkotlinx/serialization/KSerializer;)V +public final fun getItemSerializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/global/JSMap : com/intuit/player/jvm/core/bridge/NodeWrapper, java/util/Map, kotlin/jvm/internal/markers/KMappedMarker { - public static final field Companion Lcom/intuit/player/jvm/core/bridge/global/JSMap$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V - public fun clear ()V - public fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; - public fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun containsKey (Ljava/lang/Object;)Z - public fun containsValue (Ljava/lang/Object;)Z - public final fun entrySet ()Ljava/util/Set; - public fun get (Ljava/lang/Object;)Ljava/lang/Object; - public fun getEntries ()Ljava/util/Set; - public fun getKeys ()Ljava/util/Set; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getSize ()I - public fun getValues ()Ljava/util/List; - public fun isEmpty ()Z - public final fun keySet ()Ljava/util/Set; - public fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun putAll (Ljava/util/Map;)V - public fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z - public fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z - public fun replaceAll (Ljava/util/function/BiFunction;)V - public final fun size ()I - public synthetic fun values ()Ljava/util/Collection; - public final fun values ()Ljava/util/List; +public static final field Companion Lcom/intuit/player/jvm/core/bridge/global/JSMap$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V +public fun clear ()V +public fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; +public fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun containsKey (Ljava/lang/Object;)Z +public fun containsValue (Ljava/lang/Object;)Z +public final fun entrySet ()Ljava/util/Set; +public fun get (Ljava/lang/Object;)Ljava/lang/Object; +public fun getEntries ()Ljava/util/Set; +public fun getKeys ()Ljava/util/Set; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getSize ()I +public fun getValues ()Ljava/util/List; +public fun isEmpty ()Z +public final fun keySet ()Ljava/util/Set; +public fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public fun putAll (Ljava/util/Map;)V +public fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public fun remove (Ljava/lang/Object;)Ljava/lang/Object; +public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z +public fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z +public fun replaceAll (Ljava/util/function/BiFunction;)V +public final fun size ()I +public synthetic fun values ()Ljava/util/Collection; +public final fun values ()Ljava/util/List; } public final class com/intuit/player/jvm/core/bridge/global/JSMap$Companion { - public final fun serializer (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; +public final fun serializer (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/hooks/NodeHookKt { - public static final fun getCallingStackTraceElement ()Ljava/lang/StackTraceElement; +public static final fun getCallingStackTraceElement ()Ljava/lang/StackTraceElement; } public final class com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1 : com/intuit/player/jvm/core/bridge/hooks/SyncHook1, com/intuit/player/jvm/core/bridge/hooks/NodeHook { - public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;)V - public synthetic fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; - public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)V - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun init ([Lkotlinx/serialization/KSerializer;)V +public static final field Companion Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;)V +public synthetic fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; +public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)V +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun init ([Lkotlinx/serialization/KSerializer;)V +} + +public final class com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1$Companion { +public final fun serializer (Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook2 : com/intuit/hooks/SyncHook, com/intuit/player/jvm/core/bridge/hooks/NodeHook { - public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V - public synthetic fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; - public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)V - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun init ([Lkotlinx/serialization/KSerializer;)V - public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function3;)Ljava/lang/String; +public static final field Companion Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook2$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V +public synthetic fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; +public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)V +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun init ([Lkotlinx/serialization/KSerializer;)V +public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function3;)Ljava/lang/String; +} + +public final class com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook2$Companion { +public final fun serializer (Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook1 : com/intuit/hooks/SyncWaterfallHook, com/intuit/player/jvm/core/bridge/hooks/NodeHook { - public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;)V - public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun init ([Lkotlinx/serialization/KSerializer;)V - public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;)V +public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun init ([Lkotlinx/serialization/KSerializer;)V +public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; } public final class com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook2 : com/intuit/hooks/SyncWaterfallHook, com/intuit/player/jvm/core/bridge/hooks/NodeHook { - public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V - public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun init ([Lkotlinx/serialization/KSerializer;)V - public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function3;)Ljava/lang/String; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlinx/serialization/KSerializer;Lkotlinx/serialization/KSerializer;)V +public fun call (Ljava/util/HashMap;[Ljava/lang/Object;)Ljava/lang/Object; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun init ([Lkotlinx/serialization/KSerializer;)V +public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function3;)Ljava/lang/String; } public abstract class com/intuit/player/jvm/core/bridge/hooks/SyncHook1 : com/intuit/hooks/SyncHook { - public fun ()V - public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; - public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; +public fun ()V +public final fun tap (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; +public final fun tap (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; } public class com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeConfig { - public fun ()V +public fun ()V } public abstract interface class com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeContainer { - public abstract fun getFactory ()Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory; +public abstract fun getFactory ()Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory; } public abstract interface class com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory { - public abstract fun create (Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public abstract fun create (Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; } public final class com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory$DefaultImpls { - public static synthetic fun create$default (Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public static synthetic fun create$default (Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; } public final class com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactoryKt { - public static final fun config (Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory; - public static final fun getRuntimeContainers ()Ljava/util/List; - public static final fun getRuntimeFactory ()Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory; +public static final fun config (Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory; +public static final fun getRuntimeContainers ()Ljava/util/List; +public static final fun getRuntimeFactory ()Lcom/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory; } public abstract interface class com/intuit/player/jvm/core/bridge/runtime/Runtime : com/intuit/player/jvm/core/bridge/Node { - public abstract fun add (Ljava/lang/String;Ljava/lang/Object;)V - public abstract fun execute (Ljava/lang/String;)Ljava/lang/Object; - public abstract fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; - public abstract fun getScope ()Lkotlinx/coroutines/CoroutineScope; - public abstract fun release ()V - public abstract fun serialize (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; +public abstract fun add (Ljava/lang/String;Ljava/lang/Object;)V +public abstract fun execute (Ljava/lang/String;)Ljava/lang/Object; +public abstract fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; +public abstract fun getScope ()Lkotlinx/coroutines/CoroutineScope; +public abstract fun release ()V +public abstract fun serialize (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/bridge/runtime/Runtime$DefaultImpls { - public static fun getBoolean (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Boolean; - public static fun getDouble (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Double; - public static fun getFunction (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; - public static fun getInt (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Integer; - public static fun getList (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/util/List; - public static fun getLong (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Long; - public static fun getObject (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; - public static fun getString (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/String; +public static fun getBoolean (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Boolean; +public static fun getDouble (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Double; +public static fun getFunction (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Invokable; +public static fun getInt (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Integer; +public static fun getList (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/util/List; +public static fun getLong (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/Long; +public static fun getObject (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; +public static fun getString (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Ljava/lang/String; } public abstract interface class com/intuit/player/jvm/core/bridge/serialization/encoding/FunctionDecoder : kotlinx/serialization/encoding/Decoder { - public abstract fun decodeFunction ()Lcom/intuit/player/jvm/core/bridge/Invokable; +public abstract fun decodeFunction ()Lcom/intuit/player/jvm/core/bridge/Invokable; } public final class com/intuit/player/jvm/core/bridge/serialization/encoding/FunctionDecoder$DefaultImpls { - public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; } public abstract interface class com/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder : kotlinx/serialization/encoding/Encoder { - public abstract fun encodeFunction (Lcom/intuit/player/jvm/core/bridge/Invokable;)V - public abstract fun encodeFunction (Ljava/lang/Object;)V - public abstract fun encodeFunction (Lkotlin/Function;)V - public abstract fun encodeFunction (Lkotlin/reflect/KCallable;)V +public abstract fun encodeFunction (Lcom/intuit/player/jvm/core/bridge/Invokable;)V +public abstract fun encodeFunction (Ljava/lang/Object;)V +public abstract fun encodeFunction (Lkotlin/Function;)V +public abstract fun encodeFunction (Lkotlin/reflect/KCallable;)V } public final class com/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder$DefaultImpls { - public static fun beginCollection (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; - public static fun encodeFunction (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Ljava/lang/Object;)V - public static fun encodeNotNullMark (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;)V - public static fun encodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V - public static fun encodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V +public static fun beginCollection (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; +public static fun encodeFunction (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Ljava/lang/Object;)V +public static fun encodeNotNullMark (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;)V +public static fun encodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V +public static fun encodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V } public abstract interface class com/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder : com/intuit/player/jvm/core/bridge/serialization/encoding/FunctionDecoder, kotlinx/serialization/encoding/Decoder { - public abstract fun decodeNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public abstract fun decodeValue ()Ljava/lang/Object; +public abstract fun decodeNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public abstract fun decodeValue ()Ljava/lang/Object; } public final class com/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder$DefaultImpls { - public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecodersKt { - public static final fun requireNodeDecoder (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder; - public static final fun requireNodeEncoder (Lkotlinx/serialization/encoding/Encoder;)Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder; +public static final fun requireNodeDecoder (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoder; +public static final fun requireNodeEncoder (Lkotlinx/serialization/encoding/Encoder;)Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder; } public abstract interface class com/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder : com/intuit/player/jvm/core/bridge/serialization/encoding/FunctionEncoder, kotlinx/serialization/encoding/Encoder { - public abstract fun encodeNode (Lcom/intuit/player/jvm/core/bridge/Node;)V - public abstract fun encodeValue (Ljava/lang/Object;)V +public abstract fun encodeNode (Lcom/intuit/player/jvm/core/bridge/Node;)V +public abstract fun encodeValue (Ljava/lang/Object;)V } public final class com/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder$DefaultImpls { - public static fun beginCollection (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; - public static fun encodeFunction (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Ljava/lang/Object;)V - public static fun encodeNotNullMark (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;)V - public static fun encodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V - public static fun encodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V +public static fun beginCollection (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; +public static fun encodeFunction (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Ljava/lang/Object;)V +public static fun encodeNotNullMark (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;)V +public static fun encodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V +public static fun encodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/NodeEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V } public final class com/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueCompositeDecoder$DefaultImpls { - public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueCompositeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueCompositeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public static fun getCurrentValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueCompositeDecoder;)Ljava/lang/Object; +public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueCompositeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueCompositeDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun getCurrentValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueCompositeDecoder;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueDecoder$DefaultImpls { - public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; - public static fun getCurrentValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueDecoder;)Ljava/lang/Object; +public static fun decodeNullableSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun decodeSerializableValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +public static fun getCurrentValue (Lcom/intuit/player/jvm/core/bridge/serialization/encoding/RuntimeValueDecoder;)Ljava/lang/Object; } public abstract class com/intuit/player/jvm/core/bridge/serialization/format/AbstractRuntimeFormat : com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat, kotlinx/serialization/StringFormat { - public fun (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormatConfiguration;)V - public fun decodeFromString (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Ljava/lang/Object; - public fun encodeToString (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/String; - public final fun getConfig ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormatConfiguration; - public fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; - public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; - public fun registerSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V +public fun (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormatConfiguration;)V +public fun decodeFromString (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Ljava/lang/Object; +public fun encodeToString (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/String; +public final fun getConfig ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormatConfiguration; +public fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; +public fun registerSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V } public abstract interface class com/intuit/player/jvm/core/bridge/serialization/format/Builder { - public abstract fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; +public abstract fun getFormat ()Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat; } public final class com/intuit/player/jvm/core/bridge/serialization/format/BuildersKt { - public static final fun runtimeArray (Lcom/intuit/player/jvm/core/bridge/serialization/format/Builder;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun runtimeArray (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun runtimeObject (Lcom/intuit/player/jvm/core/bridge/serialization/format/Builder;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun runtimeObject (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +public static final fun runtimeArray (Lcom/intuit/player/jvm/core/bridge/serialization/format/Builder;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +public static final fun runtimeArray (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +public static final fun runtimeObject (Lcom/intuit/player/jvm/core/bridge/serialization/format/Builder;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +public static final fun runtimeObject (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } public abstract interface annotation class com/intuit/player/jvm/core/bridge/serialization/format/RuntimeBuilderDsl : java/lang/annotation/Annotation { } public final class com/intuit/player/jvm/core/bridge/serialization/format/RuntimeDecodingException : com/intuit/player/jvm/core/bridge/serialization/format/RuntimeSerializationException { - public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class com/intuit/player/jvm/core/bridge/serialization/format/RuntimeEncodingException : com/intuit/player/jvm/core/bridge/serialization/format/RuntimeSerializationException { - public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } public abstract interface class com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat : kotlinx/serialization/SerialFormat { - public abstract fun decodeFromRuntimeValue (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; - public abstract fun encodeToRuntimeValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; - public abstract fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; - public abstract fun parseToRuntimeValue (Ljava/lang/String;)Ljava/lang/Object; - public abstract fun registerSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V +public abstract fun decodeFromRuntimeValue (Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; +public abstract fun encodeToRuntimeValue (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; +public abstract fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public abstract fun parseToRuntimeValue (Ljava/lang/String;)Ljava/lang/Object; +public abstract fun registerSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V } public abstract interface class com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormatConfiguration { - public abstract fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; - public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; +public abstract fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; } public final class com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormatKt { - public static final fun registerContextualSerializer (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V - public static final fun registerSerializersModule (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/jvm/functions/Function1;)V +public static final fun registerContextualSerializer (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V +public static final fun registerSerializersModule (Lcom/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat;Lkotlin/jvm/functions/Function1;)V } public class com/intuit/player/jvm/core/bridge/serialization/format/RuntimeSerializationException : kotlinx/serialization/SerializationException { - public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class com/intuit/player/jvm/core/bridge/serialization/json/JsonPrimitiveKt { - public static final fun getValue (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/Object; - public static final fun isJsonElementDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;)Z - public static final fun isJsonElementSerializer (Lkotlinx/serialization/DeserializationStrategy;)Z - public static final fun isJsonElementSerializer (Lkotlinx/serialization/KSerializer;)Z - public static final fun isJsonElementSerializer (Lkotlinx/serialization/SerializationStrategy;)Z +public static final fun getValue (Lkotlinx/serialization/json/JsonPrimitive;)Ljava/lang/Object; +public static final fun isJsonElementDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;)Z +public static final fun isJsonElementSerializer (Lkotlinx/serialization/DeserializationStrategy;)Z +public static final fun isJsonElementSerializer (Lkotlinx/serialization/KSerializer;)Z +public static final fun isJsonElementSerializer (Lkotlinx/serialization/SerializationStrategy;)Z } public final class com/intuit/player/jvm/core/bridge/serialization/json/PrettyKt { - public static final fun getPrettyJson ()Lkotlinx/serialization/json/Json; +public static final fun getPrettyJson ()Lkotlinx/serialization/json/Json; } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/DeferKt { } public abstract class com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionLikeSerializer : kotlinx/serialization/KSerializer { - public static final field Companion Lcom/intuit/player/jvm/core/bridge/serialization/serializers/FunctionLikeSerializer$Companion; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public static final field Companion Lcom/intuit/player/jvm/core/bridge/serialization/serializers/FunctionLikeSerializer$Companion; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionLikeSerializer$Companion { - public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionSerializer : com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionLikeSerializer { - public fun ()V +public fun ()V } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/InvokableSerializer : com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionLikeSerializer { - public fun ()V +public fun ()V } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/KCallableSerializer : com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionLikeSerializer { - public fun ()V +public fun ()V } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/ModuleKt { - public static final fun getPlayerSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; +public static final fun getPlayerSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializer : kotlinx/serialization/KSerializer { - public fun ()V - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/Node; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/Node;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun ()V +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/Node; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/Node;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } public class com/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer : kotlinx/serialization/KSerializer { - public static final field Companion Lcom/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer$Companion; - public fun (Lkotlin/jvm/functions/Function1;)V - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/NodeWrapper; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public static final field Companion Lcom/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer$Companion; +public fun (Lkotlin/jvm/functions/Function1;)V +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/NodeWrapper; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/NodeWrapper;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer$Companion { - public final fun invoke (Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer; +public final fun invoke (Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer; } public abstract class com/intuit/player/jvm/core/bridge/serialization/serializers/PolymorphicNodeWrapperSerializer : com/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer { - public fun ()V - public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/NodeWrapper; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public abstract fun selectDeserializer (Lcom/intuit/player/jvm/core/bridge/Node;)Lkotlinx/serialization/KSerializer; +public fun ()V +public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/NodeWrapper; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public abstract fun selectDeserializer (Lcom/intuit/player/jvm/core/bridge/Node;)Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer : kotlinx/serialization/KSerializer { - public static final field Companion Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$Companion; - public fun ()V - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/player/PlayerException; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Throwable;)V +public static final field Companion Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$Companion; +public fun ()V +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/player/PlayerException; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Throwable;)V } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$Companion { } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement$$serializer; - public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement$$serializer; +public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun childSerializers ()[Lkotlinx/serialization/KSerializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer$SerializableStackTraceElement$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/data/DataController : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/data/DataController$Companion; - public final fun get (Ljava/lang/String;)Ljava/lang/Object; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun set (Ljava/util/List;)V - public final fun set (Ljava/util/Map;)V +public static final field Companion Lcom/intuit/player/jvm/core/data/DataController$Companion; +public final fun get (Ljava/lang/String;)Ljava/lang/Object; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun set (Ljava/util/List;)V +public final fun set (Ljava/util/Map;)V } public final class com/intuit/player/jvm/core/data/DataController$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/data/DataControllerKt { - public static final fun get (Lcom/intuit/player/jvm/core/data/DataController;)Ljava/util/Map; - public static final fun set (Lcom/intuit/player/jvm/core/data/DataController;[Lkotlin/Pair;)V +public static final fun get (Lcom/intuit/player/jvm/core/data/DataController;)Ljava/util/Map; +public static final fun set (Lcom/intuit/player/jvm/core/data/DataController;[Lkotlin/Pair;)V } public final class com/intuit/player/jvm/core/data/DataModelWithParser : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/data/DataModelWithParser$Companion; - public final fun get (Ljava/lang/String;)Ljava/lang/Object; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun set (Ljava/util/List;)V +public static final field Companion Lcom/intuit/player/jvm/core/data/DataModelWithParser$Companion; +public final fun get (Ljava/lang/String;)Ljava/lang/Object; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun set (Ljava/util/List;)V } public final class com/intuit/player/jvm/core/data/DataModelWithParser$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/data/DataModelWithParserKt { - public static final fun get (Lcom/intuit/player/jvm/core/data/DataModelWithParser;)Ljava/util/Map; - public static final fun set (Lcom/intuit/player/jvm/core/data/DataModelWithParser;[Lkotlin/Pair;)V +public static final fun get (Lcom/intuit/player/jvm/core/data/DataModelWithParser;)Ljava/util/Map; +public static final fun set (Lcom/intuit/player/jvm/core/data/DataModelWithParser;[Lkotlin/Pair;)V } public abstract interface annotation class com/intuit/player/jvm/core/experimental/ExperimentalPlayerApi : java/lang/annotation/Annotation { } public abstract class com/intuit/player/jvm/core/expressions/Expression { - public static final field Companion Lcom/intuit/player/jvm/core/expressions/Expression$Companion; +public static final field Companion Lcom/intuit/player/jvm/core/expressions/Expression$Companion; } public final class com/intuit/player/jvm/core/expressions/Expression$Collection : com/intuit/player/jvm/core/expressions/Expression { - public static final field Companion Lcom/intuit/player/jvm/core/expressions/Expression$Collection$Companion; - public fun (Ljava/util/List;)V - public fun ([Ljava/lang/String;)V - public final fun component1 ()Ljava/util/List; - public final fun copy (Ljava/util/List;)Lcom/intuit/player/jvm/core/expressions/Expression$Collection; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/expressions/Expression$Collection;Ljava/util/List;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/expressions/Expression$Collection; - public fun equals (Ljava/lang/Object;)Z - public final fun getExpressions ()Ljava/util/List; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; +public static final field Companion Lcom/intuit/player/jvm/core/expressions/Expression$Collection$Companion; +public fun (Ljava/util/List;)V +public fun ([Ljava/lang/String;)V +public final fun component1 ()Ljava/util/List; +public final fun copy (Ljava/util/List;)Lcom/intuit/player/jvm/core/expressions/Expression$Collection; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/expressions/Expression$Collection;Ljava/util/List;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/expressions/Expression$Collection; +public fun equals (Ljava/lang/Object;)Z +public final fun getExpressions ()Ljava/util/List; +public fun hashCode ()I +public fun toString ()Ljava/lang/String; } public final class com/intuit/player/jvm/core/expressions/Expression$Collection$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/expressions/Expression$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/expressions/Expression$Single : com/intuit/player/jvm/core/expressions/Expression { - public static final field Companion Lcom/intuit/player/jvm/core/expressions/Expression$Single$Companion; - public fun (Ljava/lang/String;)V - public final fun component1 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;)Lcom/intuit/player/jvm/core/expressions/Expression$Single; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/expressions/Expression$Single;Ljava/lang/String;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/expressions/Expression$Single; - public fun equals (Ljava/lang/Object;)Z - public final fun getExpression ()Ljava/lang/String; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; +public static final field Companion Lcom/intuit/player/jvm/core/expressions/Expression$Single$Companion; +public fun (Ljava/lang/String;)V +public final fun component1 ()Ljava/lang/String; +public final fun copy (Ljava/lang/String;)Lcom/intuit/player/jvm/core/expressions/Expression$Single; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/expressions/Expression$Single;Ljava/lang/String;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/expressions/Expression$Single; +public fun equals (Ljava/lang/Object;)Z +public final fun getExpression ()Ljava/lang/String; +public fun hashCode ()I +public fun toString ()Ljava/lang/String; } public final class com/intuit/player/jvm/core/expressions/Expression$Single$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/expressions/ExpressionController : com/intuit/player/jvm/core/bridge/NodeWrapper, com/intuit/player/jvm/core/expressions/ExpressionEvaluator { - public static final field Companion Lcom/intuit/player/jvm/core/expressions/ExpressionController$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public fun evaluate (Ljava/util/List;)Ljava/lang/Object; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/expressions/ExpressionController$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public fun evaluate (Ljava/util/List;)Ljava/lang/Object; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/expressions/ExpressionController$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract interface class com/intuit/player/jvm/core/expressions/ExpressionEvaluator { - public abstract fun evaluate (Ljava/util/List;)Ljava/lang/Object; +public abstract fun evaluate (Ljava/util/List;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/expressions/ExpressionEvaluatorKt { - public static final fun evaluate (Lcom/intuit/player/jvm/core/expressions/ExpressionEvaluator;Lcom/intuit/player/jvm/core/expressions/Expression;)Ljava/lang/Object; - public static final fun evaluate (Lcom/intuit/player/jvm/core/expressions/ExpressionEvaluator;Ljava/lang/String;)Ljava/lang/Object; - public static final fun evaluate (Lcom/intuit/player/jvm/core/expressions/ExpressionEvaluator;[Ljava/lang/String;)Ljava/lang/Object; +public static final fun evaluate (Lcom/intuit/player/jvm/core/expressions/ExpressionEvaluator;Lcom/intuit/player/jvm/core/expressions/Expression;)Ljava/lang/Object; +public static final fun evaluate (Lcom/intuit/player/jvm/core/expressions/ExpressionEvaluator;Ljava/lang/String;)Ljava/lang/Object; +public static final fun evaluate (Lcom/intuit/player/jvm/core/expressions/ExpressionEvaluator;[Ljava/lang/String;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/flow/Flow { - public static final field Companion Lcom/intuit/player/jvm/core/flow/Flow$Companion; - public static final field UNKNOWN_ID Ljava/lang/String; - public fun ()V - public synthetic fun (ILjava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V - public fun (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;)V - public synthetic fun (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/util/List; - public final fun component3 ()Lkotlinx/serialization/json/JsonElement; - public final fun component4 ()Lkotlinx/serialization/json/JsonElement; - public final fun component5 ()Lcom/intuit/player/jvm/core/flow/Navigation; - public final fun copy (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;)Lcom/intuit/player/jvm/core/flow/Flow; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/Flow;Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/Flow; - public fun equals (Ljava/lang/Object;)Z - public final fun getData ()Lkotlinx/serialization/json/JsonElement; - public final fun getId ()Ljava/lang/String; - public final fun getNavigation ()Lcom/intuit/player/jvm/core/flow/Navigation; - public final fun getSchema ()Lkotlinx/serialization/json/JsonElement; - public final fun getViews ()Ljava/util/List; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; - public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/Flow;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +public static final field Companion Lcom/intuit/player/jvm/core/flow/Flow$Companion; +public static final field UNKNOWN_ID Ljava/lang/String; +public fun ()V +public synthetic fun (ILjava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V +public fun (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;)V +public synthetic fun (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun component1 ()Ljava/lang/String; +public final fun component2 ()Ljava/util/List; +public final fun component3 ()Lkotlinx/serialization/json/JsonElement; +public final fun component4 ()Lkotlinx/serialization/json/JsonElement; +public final fun component5 ()Lcom/intuit/player/jvm/core/flow/Navigation; +public final fun copy (Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;)Lcom/intuit/player/jvm/core/flow/Flow; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/Flow;Ljava/lang/String;Ljava/util/List;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lcom/intuit/player/jvm/core/flow/Navigation;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/Flow; +public fun equals (Ljava/lang/Object;)Z +public final fun getData ()Lkotlinx/serialization/json/JsonElement; +public final fun getId ()Ljava/lang/String; +public final fun getNavigation ()Lcom/intuit/player/jvm/core/flow/Navigation; +public final fun getSchema ()Lkotlinx/serialization/json/JsonElement; +public final fun getViews ()Ljava/util/List; +public fun hashCode ()I +public fun toString ()Ljava/lang/String; +public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/Flow;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } public final class com/intuit/player/jvm/core/flow/Flow$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/Flow$$serializer; - public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/Flow; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/Flow;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/Flow$$serializer; +public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun childSerializers ()[Lkotlinx/serialization/KSerializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/Flow; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/Flow;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/Flow$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/FlowController : com/intuit/player/jvm/core/bridge/NodeWrapper, com/intuit/player/jvm/core/flow/Transition { - public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowController$Companion; - public final fun getCurrent ()Lcom/intuit/player/jvm/core/flow/FlowInstance; - public final fun getHooks ()Lcom/intuit/player/jvm/core/flow/FlowController$Hooks; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V +public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowController$Companion; +public final fun getCurrent ()Lcom/intuit/player/jvm/core/flow/FlowInstance; +public final fun getHooks ()Lcom/intuit/player/jvm/core/flow/FlowController$Hooks; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V } public final class com/intuit/player/jvm/core/flow/FlowController$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/FlowController$Hooks : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowController$Hooks$Companion; - public final fun getFlow ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowController$Hooks$Companion; +public final fun getFlow ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/flow/FlowController$Hooks$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/FlowException : com/intuit/player/jvm/core/player/PlayerException { } public final class com/intuit/player/jvm/core/flow/FlowInstance : com/intuit/player/jvm/core/bridge/NodeWrapper, com/intuit/player/jvm/core/flow/Transition { - public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowInstance$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun getCurrentState ()Lcom/intuit/player/jvm/core/player/state/NamedState; - public final fun getHooks ()Lcom/intuit/player/jvm/core/flow/FlowInstance$Hooks; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V +public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowInstance$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun getCurrentState ()Lcom/intuit/player/jvm/core/player/state/NamedState; +public final fun getHooks ()Lcom/intuit/player/jvm/core/flow/FlowInstance$Hooks; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V } public final class com/intuit/player/jvm/core/flow/FlowInstance$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/FlowInstance$Hooks : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowInstance$Hooks$Companion; - public final fun getBeforeTransition ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook2; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getOnEnd ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public final fun getOnStart ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public final fun getResolveTransitionNode ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook1; - public final fun getTransition ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook2; +public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowInstance$Hooks$Companion; +public final fun getBeforeTransition ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook2; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getOnEnd ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public final fun getOnStart ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public final fun getResolveTransitionNode ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook1; +public final fun getTransition ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook2; } public final class com/intuit/player/jvm/core/flow/FlowInstance$Hooks$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/FlowResult : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowResult$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public fun equals (Ljava/lang/Object;)Z - public final fun getData ()Lkotlinx/serialization/json/JsonElement; - public final fun getEndState ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowEndState; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun hashCode ()I +public static final field Companion Lcom/intuit/player/jvm/core/flow/FlowResult$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public fun equals (Ljava/lang/Object;)Z +public final fun getData ()Lkotlinx/serialization/json/JsonElement; +public final fun getEndState ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowEndState; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun hashCode ()I } public final class com/intuit/player/jvm/core/flow/FlowResult$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/FlowResult$Serializer : com/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/FlowResult$Serializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/FlowResult$Serializer; } public final class com/intuit/player/jvm/core/flow/Navigation { - public static final field Companion Lcom/intuit/player/jvm/core/flow/Navigation$Companion; - public synthetic fun (ILjava/lang/String;Ljava/util/Map;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V - public fun (Ljava/lang/String;Ljava/util/Map;)V - public synthetic fun (Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Ljava/util/Map; - public final fun copy (Ljava/lang/String;Ljava/util/Map;)Lcom/intuit/player/jvm/core/flow/Navigation; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/Navigation;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/Navigation; - public fun equals (Ljava/lang/Object;)Z - public final fun getBEGIN ()Ljava/lang/String; - public final fun getFlows ()Ljava/util/Map; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; - public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/Navigation;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +public static final field Companion Lcom/intuit/player/jvm/core/flow/Navigation$Companion; +public synthetic fun (ILjava/lang/String;Ljava/util/Map;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V +public fun (Ljava/lang/String;Ljava/util/Map;)V +public synthetic fun (Ljava/lang/String;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun component1 ()Ljava/lang/String; +public final fun component2 ()Ljava/util/Map; +public final fun copy (Ljava/lang/String;Ljava/util/Map;)Lcom/intuit/player/jvm/core/flow/Navigation; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/Navigation;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/Navigation; +public fun equals (Ljava/lang/Object;)Z +public final fun getBEGIN ()Ljava/lang/String; +public final fun getFlows ()Ljava/util/Map; +public fun hashCode ()I +public fun toString ()Ljava/lang/String; +public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/Navigation;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } public final class com/intuit/player/jvm/core/flow/Navigation$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/Navigation$$serializer; - public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/Navigation; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/Navigation;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/Navigation$$serializer; +public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun childSerializers ()[Lkotlinx/serialization/KSerializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/Navigation; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/Navigation;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/Navigation$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/NavigationFlow { - public static final field Companion Lcom/intuit/player/jvm/core/flow/NavigationFlow$Companion; - public synthetic fun (ILjava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V - public fun (Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;)V - public synthetic fun (Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lcom/intuit/player/jvm/core/expressions/Expression; - public final fun component3 ()Ljava/util/Map; - public final fun copy (Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;)Lcom/intuit/player/jvm/core/flow/NavigationFlow; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/NavigationFlow;Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/NavigationFlow; - public fun equals (Ljava/lang/Object;)Z - public final fun getOnStart ()Lcom/intuit/player/jvm/core/expressions/Expression; - public final fun getStartState ()Ljava/lang/String; - public final fun getStates ()Ljava/util/Map; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; - public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/NavigationFlow;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +public static final field Companion Lcom/intuit/player/jvm/core/flow/NavigationFlow$Companion; +public synthetic fun (ILjava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V +public fun (Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;)V +public synthetic fun (Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun component1 ()Ljava/lang/String; +public final fun component2 ()Lcom/intuit/player/jvm/core/expressions/Expression; +public final fun component3 ()Ljava/util/Map; +public final fun copy (Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;)Lcom/intuit/player/jvm/core/flow/NavigationFlow; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/NavigationFlow;Ljava/lang/String;Lcom/intuit/player/jvm/core/expressions/Expression;Ljava/util/Map;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/NavigationFlow; +public fun equals (Ljava/lang/Object;)Z +public final fun getOnStart ()Lcom/intuit/player/jvm/core/expressions/Expression; +public final fun getStartState ()Ljava/lang/String; +public final fun getStates ()Ljava/util/Map; +public fun hashCode ()I +public fun toString ()Ljava/lang/String; +public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/NavigationFlow;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } public final class com/intuit/player/jvm/core/flow/NavigationFlow$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/NavigationFlow$$serializer; - public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/NavigationFlow; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/NavigationFlow;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/NavigationFlow$$serializer; +public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun childSerializers ()[Lkotlinx/serialization/KSerializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/NavigationFlow; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/NavigationFlow;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/NavigationFlow$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract interface class com/intuit/player/jvm/core/flow/Transition { - public abstract fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V +public abstract fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V } public final class com/intuit/player/jvm/core/flow/Transition$DefaultImpls { - public static synthetic fun transition$default (Lcom/intuit/player/jvm/core/flow/Transition;Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;ILjava/lang/Object;)V +public static synthetic fun transition$default (Lcom/intuit/player/jvm/core/flow/Transition;Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;ILjava/lang/Object;)V } public final class com/intuit/player/jvm/core/flow/TransitionKt { - public static final fun forceTransition (Lcom/intuit/player/jvm/core/flow/Transition;Ljava/lang/String;)V +public static final fun forceTransition (Lcom/intuit/player/jvm/core/flow/Transition;Ljava/lang/String;)V } public final class com/intuit/player/jvm/core/flow/TransitionOptions { - public static final field Companion Lcom/intuit/player/jvm/core/flow/TransitionOptions$Companion; - public fun ()V - public synthetic fun (IZLkotlinx/serialization/internal/SerializationConstructorMarker;)V - public fun (Z)V - public synthetic fun (ZILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Z - public final fun copy (Z)Lcom/intuit/player/jvm/core/flow/TransitionOptions; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/TransitionOptions;ZILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/TransitionOptions; - public fun equals (Ljava/lang/Object;)Z - public final fun getForce ()Z - public fun hashCode ()I - public fun toString ()Ljava/lang/String; - public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/TransitionOptions;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +public static final field Companion Lcom/intuit/player/jvm/core/flow/TransitionOptions$Companion; +public fun ()V +public synthetic fun (IZLkotlinx/serialization/internal/SerializationConstructorMarker;)V +public fun (Z)V +public synthetic fun (ZILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun component1 ()Z +public final fun copy (Z)Lcom/intuit/player/jvm/core/flow/TransitionOptions; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/flow/TransitionOptions;ZILjava/lang/Object;)Lcom/intuit/player/jvm/core/flow/TransitionOptions; +public fun equals (Ljava/lang/Object;)Z +public final fun getForce ()Z +public fun hashCode ()I +public fun toString ()Ljava/lang/String; +public static final fun write$Self (Lcom/intuit/player/jvm/core/flow/TransitionOptions;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } public final class com/intuit/player/jvm/core/flow/TransitionOptions$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/TransitionOptions$$serializer; - public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/TransitionOptions; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/flow/TransitionOptions$$serializer; +public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun childSerializers ()[Lkotlinx/serialization/KSerializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/flow/TransitionOptions; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/TransitionOptions$Companion { - public final fun getForceTransition ()Lcom/intuit/player/jvm/core/flow/TransitionOptions; - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun getForceTransition ()Lcom/intuit/player/jvm/core/flow/TransitionOptions; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowActionState : com/intuit/player/jvm/core/flow/state/NavigationFlowTransitionableState, com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowActionState$Companion; - public final fun getExp ()Lcom/intuit/player/jvm/core/expressions/Expression; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowActionState$Companion; +public final fun getExp ()Lcom/intuit/player/jvm/core/expressions/Expression; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowActionState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowEndState : com/intuit/player/jvm/core/flow/state/NavigationFlowState, com/intuit/player/jvm/core/bridge/NodeWrapper, java/util/Map, kotlin/jvm/internal/markers/KMappedMarker { - public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowEndState$Companion; - public fun clear ()V - public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; - public fun computeIfAbsent (Ljava/lang/String;Ljava/util/function/Function;)Ljava/lang/Object; - public synthetic fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun computeIfPresent (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public final fun containsKey (Ljava/lang/Object;)Z - public fun containsKey (Ljava/lang/String;)Z - public fun containsValue (Ljava/lang/Object;)Z - public final fun entrySet ()Ljava/util/Set; - public final fun get (Ljava/lang/Object;)Ljava/lang/Object; - public fun get (Ljava/lang/String;)Ljava/lang/Object; - public fun getEntries ()Ljava/util/Set; - public fun getKeys ()Ljava/util/Set; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getOutcome ()Ljava/lang/String; - public fun getSize ()I - public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; - public fun getValues ()Ljava/util/Collection; - public fun isEmpty ()Z - public final fun keySet ()Ljava/util/Set; - public synthetic fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public fun merge (Ljava/lang/String;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; - public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun putAll (Ljava/util/Map;)V - public synthetic fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public fun putIfAbsent (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;)Ljava/lang/Object; - public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z - public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z - public fun replace (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public fun replace (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Z - public fun replaceAll (Ljava/util/function/BiFunction;)V - public final fun size ()I - public final fun values ()Ljava/util/Collection; +public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowEndState$Companion; +public fun clear ()V +public synthetic fun compute (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun compute (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public synthetic fun computeIfAbsent (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object; +public fun computeIfAbsent (Ljava/lang/String;Ljava/util/function/Function;)Ljava/lang/Object; +public synthetic fun computeIfPresent (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun computeIfPresent (Ljava/lang/String;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public final fun containsKey (Ljava/lang/Object;)Z +public fun containsKey (Ljava/lang/String;)Z +public fun containsValue (Ljava/lang/Object;)Z +public final fun entrySet ()Ljava/util/Set; +public final fun get (Ljava/lang/Object;)Ljava/lang/Object; +public fun get (Ljava/lang/String;)Ljava/lang/Object; +public fun getEntries ()Ljava/util/Set; +public fun getKeys ()Ljava/util/Set; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getOutcome ()Ljava/lang/String; +public fun getSize ()I +public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public fun getValues ()Ljava/util/Collection; +public fun isEmpty ()Z +public final fun keySet ()Ljava/util/Set; +public synthetic fun merge (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public fun merge (Ljava/lang/String;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object; +public synthetic fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; +public fun putAll (Ljava/util/Map;)V +public synthetic fun putIfAbsent (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public fun putIfAbsent (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; +public fun remove (Ljava/lang/Object;)Ljava/lang/Object; +public fun remove (Ljava/lang/Object;Ljava/lang/Object;)Z +public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +public synthetic fun replace (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z +public fun replace (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; +public fun replace (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Z +public fun replaceAll (Ljava/util/function/BiFunction;)V +public final fun size ()I +public final fun values ()Ljava/util/Collection; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowEndState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowExternalState : com/intuit/player/jvm/core/flow/state/NavigationFlowTransitionableState, com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowExternalState$Companion; - public final fun get (Ljava/lang/String;)Ljava/lang/Object; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowExternalState$Companion; +public final fun get (Ljava/lang/String;)Ljava/lang/Object; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowExternalState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowFlowState : com/intuit/player/jvm/core/flow/state/NavigationFlowTransitionableState, com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowFlowState$Companion; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowFlowState$Companion; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowFlowState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract class com/intuit/player/jvm/core/flow/state/NavigationFlowState : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowState$Companion; - public abstract fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowState$Companion; +public abstract fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowStateType : java/lang/Enum { - public static final field ACTION Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; - public static final field END Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; - public static final field EXTERNAL Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; - public static final field FLOW Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; - public static final field VIEW Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; - public static fun valueOf (Ljava/lang/String;)Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; - public static fun values ()[Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field ACTION Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field END Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field EXTERNAL Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field FLOW Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field VIEW Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static fun valueOf (Ljava/lang/String;)Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static fun values ()[Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; } public abstract class com/intuit/player/jvm/core/flow/state/NavigationFlowTransitionableState : com/intuit/player/jvm/core/flow/state/NavigationFlowState { - public synthetic fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getRef ()Ljava/lang/String; - public final fun getTransitions ()Ljava/util/Map; +public synthetic fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlin/jvm/internal/DefaultConstructorMarker;)V +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getRef ()Ljava/lang/String; +public final fun getTransitions ()Ljava/util/Map; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowViewState : com/intuit/player/jvm/core/flow/state/NavigationFlowTransitionableState, com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowViewState$Companion; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; +public static final field Companion Lcom/intuit/player/jvm/core/flow/state/NavigationFlowViewState$Companion; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStateType ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowStateType; } public final class com/intuit/player/jvm/core/flow/state/NavigationFlowViewState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/logger/TapableLogger : com/intuit/player/jvm/core/bridge/NodeWrapper, com/intuit/player/jvm/core/plugins/LoggerPlugin { - public static final field Companion Lcom/intuit/player/jvm/core/logger/TapableLogger$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun addHandler (Lcom/intuit/player/jvm/core/plugins/LoggerPlugin;)V - public fun apply (Lcom/intuit/player/jvm/core/player/Player;)V - public fun debug ([Ljava/lang/Object;)V - public fun error ([Ljava/lang/Object;)V - public final fun getHooks ()Lcom/intuit/player/jvm/core/logger/TapableLogger$Hooks; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun info ([Ljava/lang/Object;)V - public fun trace ([Ljava/lang/Object;)V - public fun warn ([Ljava/lang/Object;)V +public static final field Companion Lcom/intuit/player/jvm/core/logger/TapableLogger$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun addHandler (Lcom/intuit/player/jvm/core/plugins/LoggerPlugin;)V +public fun apply (Lcom/intuit/player/jvm/core/player/Player;)V +public fun debug ([Ljava/lang/Object;)V +public fun error ([Ljava/lang/Object;)V +public final fun getHooks ()Lcom/intuit/player/jvm/core/logger/TapableLogger$Hooks; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun info ([Ljava/lang/Object;)V +public fun trace ([Ljava/lang/Object;)V +public fun warn ([Ljava/lang/Object;)V } public final class com/intuit/player/jvm/core/logger/TapableLogger$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/logger/TapableLogger$Hooks : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/logger/TapableLogger$Hooks$Companion; - public final fun getDebug ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; - public final fun getError ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; - public final fun getInfo ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getTrace ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; - public final fun getWarn ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; +public static final field Companion Lcom/intuit/player/jvm/core/logger/TapableLogger$Hooks$Companion; +public final fun getDebug ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; +public final fun getError ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; +public final fun getInfo ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getTrace ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; +public final fun getWarn ()Lcom/intuit/player/jvm/core/bridge/hooks/SyncHook1; } public final class com/intuit/player/jvm/core/logger/TapableLogger$Hooks$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/managed/AsyncIterationManager { - public fun (Lcom/intuit/player/jvm/core/managed/AsyncIterator;)V - public final fun getIterator ()Lcom/intuit/player/jvm/core/managed/AsyncIterator; - public final fun getState ()Lkotlinx/coroutines/flow/StateFlow; - public final fun next (Lkotlinx/coroutines/CoroutineScope;Ljava/lang/Object;)Lkotlinx/coroutines/Job; - public static synthetic fun next$default (Lcom/intuit/player/jvm/core/managed/AsyncIterationManager;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/Object;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +public fun (Lcom/intuit/player/jvm/core/managed/AsyncIterator;)V +public final fun getIterator ()Lcom/intuit/player/jvm/core/managed/AsyncIterator; +public final fun getState ()Lkotlinx/coroutines/flow/StateFlow; +public final fun next (Lkotlinx/coroutines/CoroutineScope;Ljava/lang/Object;)Lkotlinx/coroutines/Job; +public static synthetic fun next$default (Lcom/intuit/player/jvm/core/managed/AsyncIterationManager;Lkotlinx/coroutines/CoroutineScope;Ljava/lang/Object;ILjava/lang/Object;)Lkotlinx/coroutines/Job; } public abstract class com/intuit/player/jvm/core/managed/AsyncIterationManager$State { } public final class com/intuit/player/jvm/core/managed/AsyncIterationManager$State$Done : com/intuit/player/jvm/core/managed/AsyncIterationManager$State { - public static final field INSTANCE Lcom/intuit/player/jvm/core/managed/AsyncIterationManager$State$Done; +public static final field INSTANCE Lcom/intuit/player/jvm/core/managed/AsyncIterationManager$State$Done; } public final class com/intuit/player/jvm/core/managed/AsyncIterationManager$State$Error : com/intuit/player/jvm/core/managed/AsyncIterationManager$State { - public fun (Ljava/lang/Exception;)V - public final fun getError ()Ljava/lang/Exception; +public fun (Ljava/lang/Exception;)V +public final fun getError ()Ljava/lang/Exception; } public final class com/intuit/player/jvm/core/managed/AsyncIterationManager$State$Item : com/intuit/player/jvm/core/managed/AsyncIterationManager$State { - public fun (Ljava/lang/Object;)V - public final fun getValue ()Ljava/lang/Object; +public fun (Ljava/lang/Object;)V +public final fun getValue ()Ljava/lang/Object; } public final class com/intuit/player/jvm/core/managed/AsyncIterationManager$State$NotStarted : com/intuit/player/jvm/core/managed/AsyncIterationManager$State { - public static final field INSTANCE Lcom/intuit/player/jvm/core/managed/AsyncIterationManager$State$NotStarted; +public static final field INSTANCE Lcom/intuit/player/jvm/core/managed/AsyncIterationManager$State$NotStarted; } public final class com/intuit/player/jvm/core/managed/AsyncIterationManager$State$Pending : com/intuit/player/jvm/core/managed/AsyncIterationManager$State { - public static final field INSTANCE Lcom/intuit/player/jvm/core/managed/AsyncIterationManager$State$Pending; +public static final field INSTANCE Lcom/intuit/player/jvm/core/managed/AsyncIterationManager$State$Pending; } public abstract interface class com/intuit/player/jvm/core/managed/AsyncIterator { - public static final field Companion Lcom/intuit/player/jvm/core/managed/AsyncIterator$Companion; - public abstract fun next (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun terminate (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +public static final field Companion Lcom/intuit/player/jvm/core/managed/AsyncIterator$Companion; +public abstract fun next (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +public abstract fun terminate (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/managed/AsyncIterator$Companion { - public final fun invoke (Ljava/util/List;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; - public final fun invoke ([Ljava/lang/Object;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; +public final fun invoke (Ljava/util/List;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; +public final fun invoke ([Ljava/lang/Object;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; } public final class com/intuit/player/jvm/core/managed/AsyncIterator$DefaultImpls { - public static synthetic fun next$default (Lcom/intuit/player/jvm/core/managed/AsyncIterator;Ljava/lang/Object;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static fun terminate (Lcom/intuit/player/jvm/core/managed/AsyncIterator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +public static synthetic fun next$default (Lcom/intuit/player/jvm/core/managed/AsyncIterator;Ljava/lang/Object;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +public static fun terminate (Lcom/intuit/player/jvm/core/managed/AsyncIterator;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class com/intuit/player/jvm/core/managed/AsyncIteratorKt { - public static final fun AsyncFlowIterator (Ljava/util/List;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; - public static final fun AsyncFlowIterator ([Ljava/lang/String;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; +public static final fun AsyncFlowIterator (Ljava/util/List;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; +public static final fun AsyncFlowIterator ([Ljava/lang/String;)Lcom/intuit/player/jvm/core/managed/AsyncIterator; } public final class com/intuit/player/jvm/core/player/HeadlessPlayer : com/intuit/player/jvm/core/player/Player, com/intuit/player/jvm/core/bridge/NodeWrapper { - public fun (Ljava/util/List;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;)V - public synthetic fun (Ljava/util/List;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;)V - public fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)V - public fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;)V - public synthetic fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun getHooks ()Lcom/intuit/player/jvm/core/player/Player$Hooks; - public fun getLogger ()Lcom/intuit/player/jvm/core/logger/TapableLogger; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getPlugins ()Ljava/util/List; - public final fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; - public fun getState ()Lcom/intuit/player/jvm/core/player/state/PlayerFlowState; - public fun release ()V - public final fun start (Lcom/intuit/player/jvm/core/bridge/Node;)Lcom/intuit/player/jvm/core/bridge/Completable; - public final fun start (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/Completable; - public fun start (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Completable; +public fun (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;[Lcom/intuit/player/jvm/core/plugins/Plugin;)V +public fun (Ljava/util/List;)V +public fun (Ljava/util/List;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)V +public fun (Ljava/util/List;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;)V +public synthetic fun (Ljava/util/List;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;)V +public fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)V +public fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;)V +public synthetic fun ([Lcom/intuit/player/jvm/core/plugins/Plugin;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/net/URL;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public fun getHooks ()Lcom/intuit/player/jvm/core/player/Player$Hooks; +public fun getLogger ()Lcom/intuit/player/jvm/core/logger/TapableLogger; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getPlugins ()Ljava/util/List; +public final fun getRuntime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; +public fun getState ()Lcom/intuit/player/jvm/core/player/state/PlayerFlowState; +public fun release ()V +public final fun start (Lcom/intuit/player/jvm/core/bridge/Node;)Lcom/intuit/player/jvm/core/bridge/Completable; +public final fun start (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/Completable; +public fun start (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Completable; } public final class com/intuit/player/jvm/core/player/JSPlayerConfig { - public static final field Companion Lcom/intuit/player/jvm/core/player/JSPlayerConfig$Companion; - public fun ()V - public synthetic fun (ILjava/util/List;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V - public fun (Ljava/util/List;)V - public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/List; - public final fun copy (Ljava/util/List;)Lcom/intuit/player/jvm/core/player/JSPlayerConfig; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/player/JSPlayerConfig;Ljava/util/List;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/player/JSPlayerConfig; - public fun equals (Ljava/lang/Object;)Z - public final fun getPlugins ()Ljava/util/List; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; - public static final fun write$Self (Lcom/intuit/player/jvm/core/player/JSPlayerConfig;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +public static final field Companion Lcom/intuit/player/jvm/core/player/JSPlayerConfig$Companion; +public fun ()V +public synthetic fun (ILjava/util/List;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V +public fun (Ljava/util/List;)V +public synthetic fun (Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun component1 ()Ljava/util/List; +public final fun copy (Ljava/util/List;)Lcom/intuit/player/jvm/core/player/JSPlayerConfig; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/player/JSPlayerConfig;Ljava/util/List;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/player/JSPlayerConfig; +public fun equals (Ljava/lang/Object;)Z +public final fun getPlugins ()Ljava/util/List; +public fun hashCode ()I +public fun toString ()Ljava/lang/String; +public static final fun write$Self (Lcom/intuit/player/jvm/core/player/JSPlayerConfig;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } public final class com/intuit/player/jvm/core/player/JSPlayerConfig$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/player/JSPlayerConfig$$serializer; - public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/player/JSPlayerConfig; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/player/JSPlayerConfig;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/player/JSPlayerConfig$$serializer; +public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun childSerializers ()[Lkotlinx/serialization/KSerializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/player/JSPlayerConfig; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/player/JSPlayerConfig;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/player/JSPlayerConfig$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract class com/intuit/player/jvm/core/player/Player : com/intuit/player/jvm/core/plugins/Pluggable { - public fun ()V - public abstract fun getHooks ()Lcom/intuit/player/jvm/core/player/Player$Hooks; - public abstract fun getLogger ()Lcom/intuit/player/jvm/core/logger/TapableLogger; - public abstract fun getState ()Lcom/intuit/player/jvm/core/player/state/PlayerFlowState; - public abstract fun release ()V - public abstract fun start (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Completable; - public final fun start (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/Completable; - public final fun start (Ljava/net/URL;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/Completable; - public static synthetic fun start$default (Lcom/intuit/player/jvm/core/player/Player;Ljava/net/URL;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Completable; +public fun ()V +public abstract fun getHooks ()Lcom/intuit/player/jvm/core/player/Player$Hooks; +public abstract fun getLogger ()Lcom/intuit/player/jvm/core/logger/TapableLogger; +public abstract fun getState ()Lcom/intuit/player/jvm/core/player/state/PlayerFlowState; +public abstract fun release ()V +public abstract fun start (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Completable; +public final fun start (Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/Completable; +public final fun start (Ljava/net/URL;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/bridge/Completable; +public static synthetic fun start$default (Lcom/intuit/player/jvm/core/player/Player;Ljava/net/URL;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Completable; } public abstract interface class com/intuit/player/jvm/core/player/Player$Hooks { - public static final field Companion Lcom/intuit/player/jvm/core/player/Player$Hooks$Companion; - public abstract fun getDataController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public abstract fun getExpressionEvaluator ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public abstract fun getFlowController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public abstract fun getState ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public abstract fun getValidationController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public abstract fun getView ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public abstract fun getViewController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public static final field Companion Lcom/intuit/player/jvm/core/player/Player$Hooks$Companion; +public abstract fun getDataController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public abstract fun getExpressionEvaluator ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public abstract fun getFlowController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public abstract fun getState ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public abstract fun getValidationController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public abstract fun getView ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public abstract fun getViewController ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; } public final class com/intuit/player/jvm/core/player/Player$Hooks$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public class com/intuit/player/jvm/core/player/PlayerException : java/lang/Exception { - public static final field Companion Lcom/intuit/player/jvm/core/player/PlayerException$Companion; - public fun (Ljava/lang/String;Ljava/lang/Throwable;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public static final field Companion Lcom/intuit/player/jvm/core/player/PlayerException$Companion; +public fun (Ljava/lang/String;Ljava/lang/Throwable;)V +public synthetic fun (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } public final class com/intuit/player/jvm/core/player/PlayerException$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/player/PlayerFlowStatus : java/lang/Enum { - public static final field COMPLETED Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; - public static final field Companion Lcom/intuit/player/jvm/core/player/PlayerFlowStatus$Companion; - public static final field ERROR Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; - public static final field IN_PROGRESS Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; - public static final field NOT_STARTED Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; - public static final field RELEASED Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; - public final fun getValue ()Ljava/lang/String; - public static fun valueOf (Ljava/lang/String;)Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; - public static fun values ()[Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field COMPLETED Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field Companion Lcom/intuit/player/jvm/core/player/PlayerFlowStatus$Companion; +public static final field ERROR Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field IN_PROGRESS Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field NOT_STARTED Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field RELEASED Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public final fun getValue ()Ljava/lang/String; +public static fun valueOf (Ljava/lang/String;)Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static fun values ()[Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; } public final class com/intuit/player/jvm/core/player/PlayerFlowStatus$Companion { - public final fun from (Ljava/lang/Object;)Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public final fun from (Ljava/lang/Object;)Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; } public final class com/intuit/player/jvm/core/player/state/CompletedState : com/intuit/player/jvm/core/player/state/PlayerFlowExecutionState { - public static final field Companion Lcom/intuit/player/jvm/core/player/state/CompletedState$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun getData ()Lkotlinx/serialization/json/JsonElement; - public final fun getEndState ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowEndState; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field Companion Lcom/intuit/player/jvm/core/player/state/CompletedState$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun getData ()Lkotlinx/serialization/json/JsonElement; +public final fun getEndState ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowEndState; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; } public final class com/intuit/player/jvm/core/player/state/CompletedState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/player/state/ControllerState : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/player/state/ControllerState$Companion; - public final fun getData ()Lcom/intuit/player/jvm/core/data/DataController; - public final fun getExpression ()Lcom/intuit/player/jvm/core/expressions/ExpressionController; - public final fun getFlow ()Lcom/intuit/player/jvm/core/flow/FlowController; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getValidation ()Lcom/intuit/player/jvm/core/validation/ValidationController; - public final fun getView ()Lcom/intuit/player/jvm/core/view/ViewController; +public static final field Companion Lcom/intuit/player/jvm/core/player/state/ControllerState$Companion; +public final fun getData ()Lcom/intuit/player/jvm/core/data/DataController; +public final fun getExpression ()Lcom/intuit/player/jvm/core/expressions/ExpressionController; +public final fun getFlow ()Lcom/intuit/player/jvm/core/flow/FlowController; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getValidation ()Lcom/intuit/player/jvm/core/validation/ValidationController; +public final fun getView ()Lcom/intuit/player/jvm/core/view/ViewController; } public final class com/intuit/player/jvm/core/player/state/ControllerState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract class com/intuit/player/jvm/core/player/state/ErrorState : com/intuit/player/jvm/core/player/state/PlayerFlowState { - public static final field Companion Lcom/intuit/player/jvm/core/player/state/ErrorState$Companion; - public fun ()V - public abstract fun getError ()Lcom/intuit/player/jvm/core/player/PlayerException; - public abstract fun getFlow ()Lcom/intuit/player/jvm/core/flow/Flow; - public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field Companion Lcom/intuit/player/jvm/core/player/state/ErrorState$Companion; +public fun ()V +public abstract fun getError ()Lcom/intuit/player/jvm/core/player/PlayerException; +public abstract fun getFlow ()Lcom/intuit/player/jvm/core/flow/Flow; +public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; } public final class com/intuit/player/jvm/core/player/state/ErrorState$Companion { - public final fun from (Lcom/intuit/player/jvm/core/player/PlayerException;Lcom/intuit/player/jvm/core/flow/Flow;)Lcom/intuit/player/jvm/core/player/state/ErrorState; - public final fun from (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/Flow;)Lcom/intuit/player/jvm/core/player/state/ErrorState; - public static synthetic fun from$default (Lcom/intuit/player/jvm/core/player/state/ErrorState$Companion;Lcom/intuit/player/jvm/core/player/PlayerException;Lcom/intuit/player/jvm/core/flow/Flow;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/player/state/ErrorState; - public static synthetic fun from$default (Lcom/intuit/player/jvm/core/player/state/ErrorState$Companion;Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/Flow;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/player/state/ErrorState; +public final fun from (Lcom/intuit/player/jvm/core/player/PlayerException;Lcom/intuit/player/jvm/core/flow/Flow;)Lcom/intuit/player/jvm/core/player/state/ErrorState; +public final fun from (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/Flow;)Lcom/intuit/player/jvm/core/player/state/ErrorState; +public static synthetic fun from$default (Lcom/intuit/player/jvm/core/player/state/ErrorState$Companion;Lcom/intuit/player/jvm/core/player/PlayerException;Lcom/intuit/player/jvm/core/flow/Flow;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/player/state/ErrorState; +public static synthetic fun from$default (Lcom/intuit/player/jvm/core/player/state/ErrorState$Companion;Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/Flow;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/player/state/ErrorState; } public final class com/intuit/player/jvm/core/player/state/InProgressState : com/intuit/player/jvm/core/player/state/PlayerFlowExecutionState, com/intuit/player/jvm/core/bridge/NodeWrapper, com/intuit/player/jvm/core/expressions/ExpressionEvaluator, com/intuit/player/jvm/core/flow/Transition { - public static final field Companion Lcom/intuit/player/jvm/core/player/state/InProgressState$Companion; - public fun evaluate (Ljava/util/List;)Ljava/lang/Object; - public final fun fail (Ljava/lang/Throwable;)V - public final fun getControllers ()Lcom/intuit/player/jvm/core/player/state/ControllerState; - public final fun getFlowResult ()Lcom/intuit/player/jvm/core/bridge/Completable; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; - public fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V +public static final field Companion Lcom/intuit/player/jvm/core/player/state/InProgressState$Companion; +public fun evaluate (Ljava/util/List;)Ljava/lang/Object; +public final fun fail (Ljava/lang/Throwable;)V +public final fun getControllers ()Lcom/intuit/player/jvm/core/player/state/ControllerState; +public final fun getFlowResult ()Lcom/intuit/player/jvm/core/bridge/Completable; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public fun transition (Ljava/lang/String;Lcom/intuit/player/jvm/core/flow/TransitionOptions;)V } public final class com/intuit/player/jvm/core/player/state/InProgressState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/player/state/NamedState : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/player/state/NamedState$Companion; - public final fun getName ()Ljava/lang/String; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getValue ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowState; - public fun toString ()Ljava/lang/String; +public static final field Companion Lcom/intuit/player/jvm/core/player/state/NamedState$Companion; +public final fun getName ()Ljava/lang/String; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getValue ()Lcom/intuit/player/jvm/core/flow/state/NavigationFlowState; +public fun toString ()Ljava/lang/String; } public final class com/intuit/player/jvm/core/player/state/NamedState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/player/state/NotStartedState : com/intuit/player/jvm/core/player/state/PlayerFlowState, com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/player/state/NotStartedState$Companion; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field Companion Lcom/intuit/player/jvm/core/player/state/NotStartedState$Companion; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; } public final class com/intuit/player/jvm/core/player/state/NotStartedState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract class com/intuit/player/jvm/core/player/state/PlayerFlowExecutionState : com/intuit/player/jvm/core/player/state/PlayerFlowState, com/intuit/player/jvm/core/bridge/NodeWrapper { - public synthetic fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getDataModel ()Lcom/intuit/player/jvm/core/data/DataModelWithParser; - public final fun getFlow ()Lcom/intuit/player/jvm/core/flow/Flow; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public synthetic fun (Lcom/intuit/player/jvm/core/bridge/Node;Lkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun getDataModel ()Lcom/intuit/player/jvm/core/data/DataModelWithParser; +public final fun getFlow ()Lcom/intuit/player/jvm/core/flow/Flow; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public abstract class com/intuit/player/jvm/core/player/state/PlayerFlowState : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/player/state/PlayerFlowState$Companion; - public final fun getRef ()Ljava/lang/String; - public abstract fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field Companion Lcom/intuit/player/jvm/core/player/state/PlayerFlowState$Companion; +public final fun getRef ()Ljava/lang/String; +public abstract fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; } public final class com/intuit/player/jvm/core/player/state/PlayerFlowState$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/player/state/PlayerFlowStateKt { - public static final fun fail (Lcom/intuit/player/jvm/core/player/state/InProgressState;Ljava/lang/String;)V - public static final fun getCompletedState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/CompletedState; - public static final fun getCurrentFlowState (Lcom/intuit/player/jvm/core/player/state/InProgressState;)Lcom/intuit/player/jvm/core/player/state/NamedState; - public static final fun getCurrentView (Lcom/intuit/player/jvm/core/player/state/InProgressState;)Lcom/intuit/player/jvm/core/view/View; - public static final fun getErrorState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/ErrorState; - public static final fun getInProgressState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/InProgressState; - public static final fun getLastViewUpdate (Lcom/intuit/player/jvm/core/player/state/InProgressState;)Lcom/intuit/player/jvm/core/asset/Asset; - public static final fun getNotStartedState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/NotStartedState; - public static final fun getReleasedState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/ReleasedState; +public static final fun fail (Lcom/intuit/player/jvm/core/player/state/InProgressState;Ljava/lang/String;)V +public static final fun getCompletedState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/CompletedState; +public static final fun getCurrentFlowState (Lcom/intuit/player/jvm/core/player/state/InProgressState;)Lcom/intuit/player/jvm/core/player/state/NamedState; +public static final fun getCurrentView (Lcom/intuit/player/jvm/core/player/state/InProgressState;)Lcom/intuit/player/jvm/core/view/View; +public static final fun getErrorState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/ErrorState; +public static final fun getInProgressState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/InProgressState; +public static final fun getLastViewUpdate (Lcom/intuit/player/jvm/core/player/state/InProgressState;)Lcom/intuit/player/jvm/core/asset/Asset; +public static final fun getNotStartedState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/NotStartedState; +public static final fun getReleasedState (Lcom/intuit/player/jvm/core/player/Player;)Lcom/intuit/player/jvm/core/player/state/ReleasedState; } public final class com/intuit/player/jvm/core/player/state/ReleasedState : com/intuit/player/jvm/core/player/state/PlayerFlowState, com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field INSTANCE Lcom/intuit/player/jvm/core/player/state/ReleasedState; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; +public static final field INSTANCE Lcom/intuit/player/jvm/core/player/state/ReleasedState; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public fun getStatus ()Lcom/intuit/player/jvm/core/player/PlayerFlowStatus; } public abstract interface class com/intuit/player/jvm/core/plugins/JSPluginWrapper : com/intuit/player/jvm/core/bridge/NodeWrapper, com/intuit/player/jvm/core/plugins/RuntimePlugin { - public abstract fun getInstance ()Lcom/intuit/player/jvm/core/bridge/Node; - public abstract fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public abstract fun getInstance ()Lcom/intuit/player/jvm/core/bridge/Node; +public abstract fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/plugins/JSPluginWrapper$DefaultImpls { - public static fun getNode (Lcom/intuit/player/jvm/core/plugins/JSPluginWrapper;)Lcom/intuit/player/jvm/core/bridge/Node; +public static fun getNode (Lcom/intuit/player/jvm/core/plugins/JSPluginWrapper;)Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/plugins/JSPluginWrapper$Serializer : com/intuit/player/jvm/core/bridge/serialization/serializers/NodeWrapperSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/plugins/JSPluginWrapper$Serializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/plugins/JSPluginWrapper$Serializer; } public abstract class com/intuit/player/jvm/core/plugins/JSScriptPluginWrapper : com/intuit/player/jvm/core/plugins/JSPluginWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper$Companion; - protected field instance Lcom/intuit/player/jvm/core/bridge/Node; - public fun (Ljava/lang/String;Ljava/lang/String;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun apply (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)V - public final fun buildInstance (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; - public static synthetic fun buildInstance$default (Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getInstance ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getName ()Ljava/lang/String; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - protected final fun getScript ()Ljava/lang/String; - protected final fun setInstance (Lcom/intuit/player/jvm/core/bridge/Node;)V +public static final field Companion Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper$Companion; +protected field instance Lcom/intuit/player/jvm/core/bridge/Node; +public fun (Ljava/lang/String;Ljava/lang/String;)V +public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V +public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public fun apply (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)V +public final fun buildInstance (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/Node; +public static synthetic fun buildInstance$default (Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper;Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;Ljava/lang/String;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getInstance ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getName ()Ljava/lang/String; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +protected final fun getScript ()Ljava/lang/String; +protected final fun setInstance (Lcom/intuit/player/jvm/core/bridge/Node;)V } public final class com/intuit/player/jvm/core/plugins/JSScriptPluginWrapper$Companion { - public final fun from (Ljava/lang/String;Ljava/lang/String;)Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper; - public final fun from (Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper; - public static synthetic fun from$default (Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper$Companion;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper; +public final fun from (Ljava/lang/String;Ljava/lang/String;)Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper; +public final fun from (Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper; +public static synthetic fun from$default (Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper$Companion;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper; } public final class com/intuit/player/jvm/core/plugins/JSScriptPluginWrapperKt { - public static final fun PlayerPluginException (Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper;Ljava/lang/String;)Lcom/intuit/player/jvm/core/plugins/PlayerPluginException; +public static final fun PlayerPluginException (Lcom/intuit/player/jvm/core/plugins/JSScriptPluginWrapper;Ljava/lang/String;)Lcom/intuit/player/jvm/core/plugins/PlayerPluginException; } public abstract interface class com/intuit/player/jvm/core/plugins/LoggerPlugin : com/intuit/player/jvm/core/plugins/PlayerPlugin { - public abstract fun apply (Lcom/intuit/player/jvm/core/player/Player;)V - public abstract fun debug ([Ljava/lang/Object;)V - public abstract fun error ([Ljava/lang/Object;)V - public abstract fun info ([Ljava/lang/Object;)V - public abstract fun trace ([Ljava/lang/Object;)V - public abstract fun warn ([Ljava/lang/Object;)V +public abstract fun apply (Lcom/intuit/player/jvm/core/player/Player;)V +public abstract fun debug ([Ljava/lang/Object;)V +public abstract fun error ([Ljava/lang/Object;)V +public abstract fun info ([Ljava/lang/Object;)V +public abstract fun trace ([Ljava/lang/Object;)V +public abstract fun warn ([Ljava/lang/Object;)V } public final class com/intuit/player/jvm/core/plugins/LoggerPlugin$DefaultImpls { - public static fun apply (Lcom/intuit/player/jvm/core/plugins/LoggerPlugin;Lcom/intuit/player/jvm/core/player/Player;)V +public static fun apply (Lcom/intuit/player/jvm/core/plugins/LoggerPlugin;Lcom/intuit/player/jvm/core/player/Player;)V } public final class com/intuit/player/jvm/core/plugins/LoggerPluginKt { - public static final fun getLogger (Lcom/intuit/player/jvm/core/plugins/Pluggable;)Lcom/intuit/player/jvm/core/plugins/LoggerPlugin; +public static final fun getLogger (Lcom/intuit/player/jvm/core/plugins/Pluggable;)Lcom/intuit/player/jvm/core/plugins/LoggerPlugin; } public abstract interface class com/intuit/player/jvm/core/plugins/PlayerPlugin : com/intuit/player/jvm/core/plugins/Plugin { - public abstract fun apply (Lcom/intuit/player/jvm/core/player/Player;)V +public abstract fun apply (Lcom/intuit/player/jvm/core/player/Player;)V } public final class com/intuit/player/jvm/core/plugins/PlayerPluginException : com/intuit/player/jvm/core/player/PlayerException { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getPluginName ()Ljava/lang/String; +public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)V +public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun getPluginName ()Ljava/lang/String; } public abstract interface class com/intuit/player/jvm/core/plugins/Pluggable { - public abstract fun getPlugins ()Ljava/util/List; +public abstract fun getPlugins ()Ljava/util/List; } public abstract interface class com/intuit/player/jvm/core/plugins/Plugin { } public final class com/intuit/player/jvm/core/plugins/PluginKt { - public static final fun warnPluginNotFound (Lcom/intuit/player/jvm/core/player/Player;Ljava/lang/String;)Lcom/intuit/player/jvm/core/plugins/Plugin; +public static final fun warnPluginNotFound (Lcom/intuit/player/jvm/core/player/Player;Ljava/lang/String;)Lcom/intuit/player/jvm/core/plugins/Plugin; } public abstract interface class com/intuit/player/jvm/core/plugins/RuntimePlugin : com/intuit/player/jvm/core/plugins/Plugin { - public abstract fun apply (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)V +public abstract fun apply (Lcom/intuit/player/jvm/core/bridge/runtime/Runtime;)V } public class com/intuit/player/jvm/core/plugins/logging/PlayerLoggingConfig { - public fun ()V +public fun ()V } public abstract interface class com/intuit/player/jvm/core/plugins/logging/PlayerLoggingContainer { - public abstract fun getFactory ()Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory; +public abstract fun getFactory ()Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory; } public abstract interface class com/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory { - public abstract fun create (Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/plugins/LoggerPlugin; +public abstract fun create (Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/plugins/LoggerPlugin; } public final class com/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory$DefaultImpls { - public static synthetic fun create$default (Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/plugins/LoggerPlugin; +public static synthetic fun create$default (Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/plugins/LoggerPlugin; } public final class com/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactoryKt { - public static final fun config (Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory; - public static final fun getLoggerContainers ()Ljava/util/List; - public static final fun getLoggers ()Ljava/util/List; +public static final fun config (Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory;Lkotlin/jvm/functions/Function1;)Lcom/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory; +public static final fun getLoggerContainers ()Ljava/util/List; +public static final fun getLoggers ()Ljava/util/List; } public final class com/intuit/player/jvm/core/resolver/ResolveOptions : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/resolver/ResolveOptions$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getValidation ()Lcom/intuit/player/jvm/core/validation/Validation; +public static final field Companion Lcom/intuit/player/jvm/core/resolver/ResolveOptions$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getValidation ()Lcom/intuit/player/jvm/core/validation/Validation; } public final class com/intuit/player/jvm/core/resolver/ResolveOptions$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/resolver/Resolver : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/resolver/Resolver$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun getHooks ()Lcom/intuit/player/jvm/core/resolver/Resolver$Hooks; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/resolver/Resolver$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun getHooks ()Lcom/intuit/player/jvm/core/resolver/Resolver$Hooks; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/resolver/Resolver$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/resolver/Resolver$Hooks : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/resolver/Resolver$Hooks$Companion; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getResolveOptions ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook2; +public static final field Companion Lcom/intuit/player/jvm/core/resolver/Resolver$Hooks$Companion; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getResolveOptions ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook2; } public final class com/intuit/player/jvm/core/resolver/Resolver$Hooks$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public abstract interface annotation class com/intuit/player/jvm/core/utils/InternalPlayerApi : java/lang/annotation/Annotation { } public final class com/intuit/player/jvm/core/validation/BindingInstance : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/validation/BindingInstance$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun asString ()Ljava/lang/String; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun parent ()Lcom/intuit/player/jvm/core/validation/BindingInstance; - public fun toString ()Ljava/lang/String; +public static final field Companion Lcom/intuit/player/jvm/core/validation/BindingInstance$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun asString ()Ljava/lang/String; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun parent ()Lcom/intuit/player/jvm/core/validation/BindingInstance; +public fun toString ()Ljava/lang/String; } public final class com/intuit/player/jvm/core/validation/BindingInstance$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/validation/ErrorValidationResponse : com/intuit/player/jvm/core/validation/ValidationResponse { - public static final field Companion Lcom/intuit/player/jvm/core/validation/ErrorValidationResponse$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/validation/ErrorValidationResponse$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/validation/ErrorValidationResponse$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/validation/Validation : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/validation/Validation$Companion; - public final fun getAll ()Lcom/intuit/player/jvm/core/bridge/global/JSMap; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/validation/Validation$Companion; +public final fun getAll ()Lcom/intuit/player/jvm/core/bridge/global/JSMap; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/validation/Validation$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/validation/ValidationController : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/validation/ValidationController$Companion; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun validateView ()Lcom/intuit/player/jvm/core/validation/ValidationInfo; +public static final field Companion Lcom/intuit/player/jvm/core/validation/ValidationController$Companion; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun validateView ()Lcom/intuit/player/jvm/core/validation/ValidationInfo; } public final class com/intuit/player/jvm/core/validation/ValidationController$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/validation/ValidationInfo { - public static final field Companion Lcom/intuit/player/jvm/core/validation/ValidationInfo$Companion; - public synthetic fun (IZLcom/intuit/player/jvm/core/bridge/global/JSMap;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V - public fun (ZLcom/intuit/player/jvm/core/bridge/global/JSMap;)V - public synthetic fun (ZLcom/intuit/player/jvm/core/bridge/global/JSMap;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Z - public final fun component2 ()Lcom/intuit/player/jvm/core/bridge/global/JSMap; - public final fun copy (ZLcom/intuit/player/jvm/core/bridge/global/JSMap;)Lcom/intuit/player/jvm/core/validation/ValidationInfo; - public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/validation/ValidationInfo;ZLcom/intuit/player/jvm/core/bridge/global/JSMap;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/validation/ValidationInfo; - public fun equals (Ljava/lang/Object;)Z - public final fun getCanTransition ()Z - public final fun getValidations ()Lcom/intuit/player/jvm/core/bridge/global/JSMap; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; - public static final fun write$Self (Lcom/intuit/player/jvm/core/validation/ValidationInfo;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +public static final field Companion Lcom/intuit/player/jvm/core/validation/ValidationInfo$Companion; +public synthetic fun (IZLcom/intuit/player/jvm/core/bridge/global/JSMap;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V +public fun (ZLcom/intuit/player/jvm/core/bridge/global/JSMap;)V +public synthetic fun (ZLcom/intuit/player/jvm/core/bridge/global/JSMap;ILkotlin/jvm/internal/DefaultConstructorMarker;)V +public final fun component1 ()Z +public final fun component2 ()Lcom/intuit/player/jvm/core/bridge/global/JSMap; +public final fun copy (ZLcom/intuit/player/jvm/core/bridge/global/JSMap;)Lcom/intuit/player/jvm/core/validation/ValidationInfo; +public static synthetic fun copy$default (Lcom/intuit/player/jvm/core/validation/ValidationInfo;ZLcom/intuit/player/jvm/core/bridge/global/JSMap;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/validation/ValidationInfo; +public fun equals (Ljava/lang/Object;)Z +public final fun getCanTransition ()Z +public final fun getValidations ()Lcom/intuit/player/jvm/core/bridge/global/JSMap; +public fun hashCode ()I +public fun toString ()Ljava/lang/String; +public static final fun write$Self (Lcom/intuit/player/jvm/core/validation/ValidationInfo;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V } public final class com/intuit/player/jvm/core/validation/ValidationInfo$$serializer : kotlinx/serialization/internal/GeneratedSerializer { - public static final field INSTANCE Lcom/intuit/player/jvm/core/validation/ValidationInfo$$serializer; - public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun childSerializers ()[Lkotlinx/serialization/KSerializer; - public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/validation/ValidationInfo; - public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; - public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; - public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/validation/ValidationInfo;)V - public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V - public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +public static final field INSTANCE Lcom/intuit/player/jvm/core/validation/ValidationInfo$$serializer; +public static final synthetic field descriptor Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun childSerializers ()[Lkotlinx/serialization/KSerializer; +public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/intuit/player/jvm/core/validation/ValidationInfo; +public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; +public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; +public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/intuit/player/jvm/core/validation/ValidationInfo;)V +public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V +public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/validation/ValidationInfo$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/validation/ValidationKt { - public static final fun getWarningsAndErrors (Lcom/intuit/player/jvm/core/validation/Validation;)Lcom/intuit/player/jvm/core/bridge/global/JSMap; +public static final fun getWarningsAndErrors (Lcom/intuit/player/jvm/core/validation/Validation;)Lcom/intuit/player/jvm/core/bridge/global/JSMap; } public abstract class com/intuit/player/jvm/core/validation/ValidationResponse : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/validation/ValidationResponse$Companion; - public final fun getMessage ()Ljava/lang/String; - public final fun getParameters ()Ljava/util/Map; +public static final field Companion Lcom/intuit/player/jvm/core/validation/ValidationResponse$Companion; +public final fun getMessage ()Ljava/lang/String; +public final fun getParameters ()Ljava/util/Map; } public final class com/intuit/player/jvm/core/validation/ValidationResponse$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/validation/WarningValidationResponse : com/intuit/player/jvm/core/validation/ValidationResponse { - public static final field Companion Lcom/intuit/player/jvm/core/validation/WarningValidationResponse$Companion; - public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V - public final fun dismiss ()V - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/validation/WarningValidationResponse$Companion; +public fun (Lcom/intuit/player/jvm/core/bridge/Node;)V +public final fun dismiss ()V +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/validation/WarningValidationResponse$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/view/View : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/view/View$Companion; - public final fun getHooks ()Lcom/intuit/player/jvm/core/view/ViewHooks; - public final fun getLastUpdate ()Lcom/intuit/player/jvm/core/asset/Asset; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/view/View$Companion; +public final fun getHooks ()Lcom/intuit/player/jvm/core/view/ViewHooks; +public final fun getLastUpdate ()Lcom/intuit/player/jvm/core/asset/Asset; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/view/View$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/view/ViewController : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/view/ViewController$Companion; - public final fun getCurrentView ()Lcom/intuit/player/jvm/core/view/View; - public final fun getHooks ()Lcom/intuit/player/jvm/core/view/ViewController$Hooks; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public static final field Companion Lcom/intuit/player/jvm/core/view/ViewController$Companion; +public final fun getCurrentView ()Lcom/intuit/player/jvm/core/view/View; +public final fun getHooks ()Lcom/intuit/player/jvm/core/view/ViewController$Hooks; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; } public final class com/intuit/player/jvm/core/view/ViewController$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/view/ViewController$Hooks : com/intuit/player/jvm/core/bridge/NodeWrapper { - public static final field Companion Lcom/intuit/player/jvm/core/view/ViewController$Hooks$Companion; - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getView ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public static final field Companion Lcom/intuit/player/jvm/core/view/ViewController$Hooks$Companion; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getView ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; } public final class com/intuit/player/jvm/core/view/ViewController$Hooks$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; +public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class com/intuit/player/jvm/core/view/ViewHooks : com/intuit/player/jvm/core/bridge/NodeWrapper { - public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; - public final fun getOnUpdate ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; - public final fun getResolver ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public fun getNode ()Lcom/intuit/player/jvm/core/bridge/Node; +public final fun getOnUpdate ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; +public final fun getResolver ()Lcom/intuit/player/jvm/core/bridge/hooks/NodeSyncHook1; } - diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/Asset.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/Asset.kt index af3402da1..ec866ed56 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/Asset.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/Asset.kt @@ -2,9 +2,13 @@ package com.intuit.player.jvm.core.asset import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getSerializable +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.player.PlayerException import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.nullable +import kotlinx.serialization.builtins.serializer /** Convenience helper to get some [value] from an [Asset] */ public val Asset.value: String? get() = get("value") as? String @@ -12,16 +16,20 @@ public val Asset.value: String? get() = get("value") as? String /** Special [Node] type that also describes a player asset */ @Serializable(with = Asset.Serializer::class) public open class Asset(override val node: Node) : Node by node, NodeWrapper { - public val id: String get() = getString("id") ?: throw PlayerException("Asset's must be described with an ID") + public val id: String by NodeSerializableField(String.serializer(), NodeSerializableField.CacheStrategy.Full) { + throw PlayerException("Asset's must be described with an ID") + } public operator fun component1(): String = id - public val type: String get() = getString("type") ?: throw PlayerException("Asset's must be described with a type") + public val type: String by NodeSerializableField(String.serializer(), NodeSerializableField.CacheStrategy.Full) { + throw PlayerException("Asset's must be described with a type") + } public operator fun component2(): String = type - public val metaData: MetaData? get() = getObject("metaData")?.let(::MetaData) + public val metaData: MetaData? by NodeSerializableField(MetaData.serializer().nullable) public operator fun component3(): MetaData? = metaData - public fun getAsset(name: String): AssetWrapper? = getObject(name)?.let(::AssetWrapper) + public fun getAsset(name: String): AssetWrapper? = getSerializable(name) override fun equals(other: Any?): Boolean = node == other diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/AssetWrapper.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/AssetWrapper.kt index 4ce4d9fd2..0ccfc01ac 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/AssetWrapper.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/AssetWrapper.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.asset import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.player.PlayerException import kotlinx.serialization.Serializable @@ -11,8 +12,9 @@ import kotlinx.serialization.Serializable */ @Serializable(with = AssetWrapper.Serializer::class) public class AssetWrapper(override val node: Node) : NodeWrapper { - public val asset: Asset get() = node.getObject("asset") as? Asset - ?: throw PlayerException("AssetWrapper is not wrapping a valid asset") + public val asset: Asset by NodeSerializableField(Asset.serializer()) { + throw PlayerException("AssetWrapper is not wrapping a valid asset") + } internal object Serializer : NodeWrapperSerializer(::AssetWrapper) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/MetaData.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/MetaData.kt index a594865a4..b777975c4 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/MetaData.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/asset/MetaData.kt @@ -2,22 +2,30 @@ package com.intuit.player.jvm.core.asset import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper -import com.intuit.player.jvm.core.bridge.getJson +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.nullable +import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull /** * Base structure containing common metadata fields. * [node] is exposed to enable retrieval of other fields. * This could also be done by extending this class. */ +@Serializable(MetaData.Serializer::class) public open class MetaData(override val node: Node) : NodeWrapper { /** [JsonElement] representation of some [beacon] description */ - public val beacon: JsonElement get() = node.getJson("beacon") + public val beacon: JsonElement by NodeSerializableField(JsonElement.serializer()) { JsonNull } /** Common metadata element used to designate how an asset should present itself by describing the intent */ - public val role: String? get() = node.getString("role") + public val role: String? by NodeSerializableField(String.serializer().nullable) /** [ref] is typically used to describe a reference to some external location */ - public val ref: String? get() = node.getString("ref") + public val ref: String? by NodeSerializableField(String.serializer().nullable) + + internal object Serializer : NodeWrapperSerializer(::MetaData) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Invokable.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Invokable.kt index 2f00cbd9b..6fef3ba15 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Invokable.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Invokable.kt @@ -2,12 +2,45 @@ package com.intuit.player.jvm.core.bridge import kotlin.jvm.functions.FunctionN +public fun Invokable(block: (args: Array) -> R): Invokable = object : Invokable { + override fun invoke(vararg args: Any?): R { + return block(args) + } +} + /** [Function] extension that provides loosely-typed vararg [invoke] signature */ -public fun interface Invokable : Function { +public interface Invokable : Function, Function0, Function1, Function2, Function3, Function4, Function5, Function6, Function7, Function8, Function9, Function10, Function11, Function12, Function13, Function14, Function15, Function16, Function17, Function18, Function19, Function20, Function21, Function22 { public operator fun invoke(vararg args: Any?): R + override fun invoke(): R = invoke(*arrayOf()) + override fun invoke(p1: Any?): R = invoke(*arrayOf(p1)) + override fun invoke(p1: Any?, p2: Any?): R = invoke(*arrayOf(p1, p2)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?): R = invoke(*arrayOf(p1, p2, p3)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?): R = invoke(*arrayOf(p1, p2, p3, p4)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, p20: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, p20: Any?, p21: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21)) + override fun invoke(p1: Any?, p2: Any?, p3: Any?, p4: Any?, p5: Any?, p6: Any?, p7: Any?, p8: Any?, p9: Any?, p10: Any?, p11: Any?, p12: Any?, p13: Any?, p14: Any?, p15: Any?, p16: Any?, p17: Any?, p18: Any?, p19: Any?, p20: Any?, p21: Any?, p22: Any?): R = invoke(*arrayOf(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22)) } /** Extension to convert an [Invokable] to some [functionTypeName] */ +@Deprecated( + "Invokable extend Functions automatically", + level = DeprecationLevel.ERROR, +) public fun Invokable.toFunction(functionTypeName: String): Function = when (functionTypeName) { "Function0" -> object : Function0 { override fun invoke() = this@toFunction() diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/isUndefined.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/IsUndefined.kt similarity index 100% rename from jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/isUndefined.kt rename to jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/IsUndefined.kt diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/JSErrorException.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/JSErrorException.kt index 1c891543b..30b7bdc7a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/JSErrorException.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/JSErrorException.kt @@ -1,11 +1,19 @@ package com.intuit.player.jvm.core.bridge +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.ThrowableSerializer import com.intuit.player.jvm.core.player.PlayerException +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.serializer -public class JSErrorException(override val node: Node, cause: Throwable? = null) : PlayerException(node.getString("message") ?: "", cause), NodeWrapper { +public class JSErrorException( + override val node: Node, + cause: Throwable? = node.getSerializable("innerErrors", ListSerializer(ThrowableSerializer()))?.first(), +) : PlayerException(node.getString("message") ?: "", cause), NodeWrapper { - public val name: String get() = node.getString("name") ?: "Error" + public val name: String by NodeSerializableField(String.serializer()) { + "Error" + } - override val message: String - get() = "$name: ${super.message}" + override val message: String = "$name: ${super.message}" } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Node.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Node.kt index 104e9114b..071da0aff 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Node.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Node.kt @@ -4,7 +4,10 @@ import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat import com.intuit.player.jvm.core.bridge.serialization.format.serializer +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializer +import com.intuit.player.jvm.core.experimental.ExperimentalPlayerApi import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull @@ -50,6 +53,14 @@ public interface Node : Map { * Returns the value corresponding to the given [key] as a [Invokable], * or `null` if such a key is not present in the node or if the value is not invokable */ + public fun getInvokable(key: String, deserializationStrategy: DeserializationStrategy): Invokable? = get(key).safeCast() + + @Deprecated( + "Replaced with getInvokable, which requires a deserializer for the return type. Either provide a deserializer explicitly, " + + "or use the extension to automatically determine the correct serializer.", + ReplaceWith("getInvokable(key)", "com.intuit.player.jvm.core.bridge.getInvokable"), + DeprecationLevel.WARNING, + ) public fun getFunction(key: String): Invokable? = get(key).safeCast() /** @@ -91,6 +102,10 @@ public interface Node : Map { public val runtime: Runtime<*> public val format: RuntimeFormat<*> get() = runtime.format + + public companion object { + public fun serializer(): KSerializer = NodeSerializer() + } } public val NodeWrapper.runtime: Runtime<*> get() = node.runtime @@ -115,6 +130,49 @@ public inline fun Node.getSerializable(key: String, serializer: Dese getSerializable(key, it) } ?: getSerializable(key) +public inline fun Node.getInvokable(key: String): Invokable? = getInvokable(key, format.serializer()) + +public inline fun Node.getInvokable(key: String, deserializer: DeserializationStrategy?): Invokable? = deserializer?.let { + getInvokable(key, it) +} ?: getInvokable(key) + +/** +* Get the toString value of a symbol. Due to the current limitations of the +* various supported JS runtimes, we can only reliably get a [String] +* representation of symbols that are contained within a parent [Node]. +* This inherently breaks the uniqueness of symbol representation on the JVM, +* and thus shouldn't be used to verify referential equality between symbols. +*/ +@ExperimentalPlayerApi +public fun Node.getSymbol(key: String): String? { + if (!runtime.containsKey("getSymbol")) { + runtime.execute( + """ + function getSymbol(parent, key) { + const value = parent[key]; + if (typeof value === 'symbol') return value.toString(); + else return null; + } + """, + ) + } + + val getSymbol = runtime.getInvokable("getSymbol") + ?: throw runtime.PlayerRuntimeException("getSymbol doesn't exist in runtime") + + return getSymbol(this, key) +} + +/** Decode a [Node] entirely as a snapshot of its current data. Will allow for additional data access after a [Node]s runtime has been released */ +@ExperimentalPlayerApi +internal fun Node.snapshot(): Map = entries.associate { (key, value) -> + key to when (value) { + is Node -> value.snapshot() + is Function<*> -> null + else -> value + } +} + private inline fun Any?.safeCast(): T? = this as? T internal object EmptyNode : Node { diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Promise.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Promise.kt index dd94059ed..37d87d5fe 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Promise.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/Promise.kt @@ -10,8 +10,10 @@ import com.intuit.player.jvm.core.utils.InternalPlayerApi import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.KSerializer @@ -39,7 +41,7 @@ public class Promise(override val node: Node) : NodeWrapper { */ public fun then(deserializer: DeserializationStrategy, block: (Expected?) -> Next?): Promise = Promise( - node.getFunction("then")?.invoke( + node.getInvokable("then")?.invoke( { arg: Any? -> try { block( @@ -51,17 +53,21 @@ public class Promise(override val node: Node) : NodeWrapper { // the value, since that _would_ be wrapped in a holder able to be generically // deserialized is Node -> arg.deserialize(deserializer) - else -> if (deserializer.isJsonElementSerializer) Json.encodeToJsonElement( - GenericSerializer(), - arg, - ) else arg - } as? Expected + else -> if (deserializer.isJsonElementSerializer) { + Json.encodeToJsonElement( + GenericSerializer(), + arg, + ) + } else { + arg + } + } as? Expected, ) } catch (e: Throwable) { Promise.reject(e) } - } - ) ?: throw PromiseException("then did not return valid Promise") + }, + ) ?: throw PromiseException("then did not return valid Promise"), ) /** @@ -74,12 +80,15 @@ public class Promise(override val node: Node) : NodeWrapper { */ public fun catch(block: (Throwable) -> Next?): Promise = Promise( - node.getFunction("catch")?.invoke( + node.getInvokable("catch")?.invoke( { arg: Any? -> try { // Attempt to dynamically build exception from JS error - if (arg is Exception) block(arg) else + if (arg is Exception) { + block(arg) + } else { block((arg as Node).deserialize()) + } } catch (e: Exception) { // fallback to simple String representation of error // if you are debugging and the stacktrace leads you @@ -87,8 +96,8 @@ public class Promise(override val node: Node) : NodeWrapper { // be very helpful block(PromiseException(arg.toString())) } - } - ) ?: throw PromiseException("then did not return valid Promise") + }, + ) ?: throw PromiseException("then did not return valid Promise"), ) /** Converts the [Promise] into a [Completable] to enable awaiting it to resolve or reject */ @@ -106,7 +115,7 @@ public class Promise(override val node: Node) : NodeWrapper { try { onComplete { when { - it.isSuccess -> offer(it.getOrNull()) + it.isSuccess -> trySend(it.getOrNull()) it.isFailure -> close(it.exceptionOrNull()) } } @@ -149,28 +158,33 @@ public val Node.Promise: Promise.Api get() = runtime.Promise public val Runtime<*>.Promise: Promise.Api get() = getObject("Promise")?.let { promise -> object : Promise.Api { override fun resolve(vararg values: T): Promise = promise - .getFunction("resolve")?.invoke(*values)?.let(::Promise) + .getInvokable("resolve")?.invoke(*values)?.let(::Promise) ?: throw PromiseException("Could not resolve with values: $values") override fun reject(vararg values: T): Promise = promise - .getFunction("reject")?.invoke(*values)?.let(::Promise) + .getInvokable("reject")?.invoke(*values)?.let(::Promise) ?: throw PromiseException("Could not reject with values: $values") } } ?: throw PlayerRuntimeException("'Promise' not defined in runtime") /** Helper to bridge complex [Promise] logic with the JS promise constructor */ -public fun Runtime<*>.Promise(block: ((T) -> Unit, (Throwable) -> Unit) -> Unit): Promise { +public fun Runtime<*>.Promise(block: suspend ((T) -> Unit, (Throwable) -> Unit) -> Unit): Promise { val key = "promiseHandler_${UUID.randomUUID().toString().replace("-", "")}" add(key) { resolve: Invokable, reject: Invokable -> - try { - block({ resolve(it) }, { reject(it) }) - } catch (e: Throwable) { - reject(e) + runtime.scope.launch { + try { + block({ runtime.scope.ensureActive(); resolve(it) }, { runtime.scope.ensureActive(); reject(it) }) + } catch (e: Throwable) { + runtime.scope.ensureActive() + reject(e) + } } + + Unit } return Promise( execute("(new Promise($key))") as? Node - ?: throw PromiseException("Error creating promise") + ?: throw PromiseException("Error creating promise"), ) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSIterator.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSIterator.kt index e5e01a47e..4565ab651 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSIterator.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSIterator.kt @@ -18,8 +18,7 @@ public class JSIterator(override val node: Node, public val itemSerializer: K ) private fun getNext(): NextResult = node - .getFunction("next")!!() - .deserialize(NextResult.serializer(itemSerializer)) + .getInvokable("next", NextResult.serializer(itemSerializer))!!() /** Always keeps track of value to be read */ private var current: NextResult = getNext() diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSMap.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSMap.kt index 10a9e0da0..4ba83447a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSMap.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/global/JSMap.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.bridge.global import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable @@ -10,11 +11,11 @@ import kotlinx.serialization.Serializable public class JSMap (override val node: Node, keySerializer: KSerializer, valueSerializer: KSerializer) : Map, NodeWrapper { override val keys: Set by lazy { - JSIterator(node.getFunction("keys")!!(), keySerializer).asSequence().toSet() + JSIterator(node.getInvokable("keys")!!(), keySerializer).asSequence().toSet() } override val values: List by lazy { - JSIterator(node.getFunction("values")!!(), valueSerializer).asSequence().toList() + JSIterator(node.getInvokable("values")!!(), valueSerializer).asSequence().toList() } override val entries: Set> by lazy { diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeHook.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeHook.kt index f81ce6a50..2de2f9372 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeHook.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeHook.kt @@ -4,6 +4,7 @@ import com.intuit.hooks.HookContext import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.utils.InternalPlayerApi import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer @@ -14,18 +15,23 @@ internal interface NodeHook : NodeWrapper { @OptIn(ExperimentalSerializationApi::class) fun init(vararg serializers: KSerializer<*>) { - node.getFunction("tap")?.invoke( + node.getInvokable("tap")?.invoke( mapOf("name" to "name", "context" to true), Invokable { args -> val context = args[0] as Map val rest = args.drop(1).zip(serializers).map { (value, serializer) -> - if (serializer.descriptor.isNullable && value == null) value - else if (value!!::class == serializer.descriptor.capturedKClass) value - else if (value is Node) value.deserialize(serializer) - else value + if (serializer.descriptor.isNullable && value == null) { + value + } else if (value!!::class == serializer.descriptor.capturedKClass) { + value + } else if (value is Node) { + value.deserialize(serializer) + } else { + value + } } call(HashMap(context), rest.toTypedArray()) - } + }, ) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHook1.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHook1.kt new file mode 100644 index 000000000..9aa3be85c --- /dev/null +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHook1.kt @@ -0,0 +1,36 @@ +package com.intuit.player.jvm.core.bridge.hooks + +import com.intuit.hooks.BailResult +import com.intuit.hooks.HookContext +import com.intuit.hooks.SyncBailHook +import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable + +@Serializable(with = NodeSyncBailHook1.Serializer::class) +public class NodeSyncBailHook1( + override val node: Node, + private val serializer1: KSerializer, +) : SyncBailHook<(HookContext, T) -> BailResult, R>(), NodeHook { + + init { init(serializer1) } + + override fun call(context: HookContext, serializedArgs: Array): R? { + require(serializedArgs.size == 1) + val p1 = serializedArgs[0] as T + return call( + { f, t -> f(context, p1) }, + ) { Unit as R } + } + + public fun tap(name: String, callback: (T?) -> BailResult): String? = super.tap(name) { _, p1 -> callback(p1) } + + public inline fun tap(noinline callback: (T?) -> BailResult): String? = tap(callingStackTraceElement.toString(), callback) + + public inline fun tap(noinline callback: (HookContext, T?) -> BailResult): String? = tap(callingStackTraceElement.toString(), callback) + + internal class Serializer(private val serializer1: KSerializer, private val serializer2: KSerializer) : NodeWrapperSerializer>({ + NodeSyncBailHook1(it, serializer1) + }) +} diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook.kt index 30e63a348..f9b334537 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHook.kt @@ -3,7 +3,9 @@ package com.intuit.player.jvm.core.bridge.hooks import com.intuit.hooks.HookContext import com.intuit.hooks.SyncHook import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable public abstract class SyncHook1 : SyncHook<(HookContext, T1) -> Unit>() { public fun tap(name: String, callback: (T1) -> Unit): String? = super.tap(name) { _, p1 -> callback(p1) } @@ -11,22 +13,25 @@ public abstract class SyncHook1 : SyncHook<(HookContext, T1) -> Unit>() { public inline fun tap(noinline callback: (HookContext, T1) -> Unit): String? = tap(callingStackTraceElement.toString(), callback) } -// TODO: Work with serializable +@Serializable(with = NodeSyncHook1.Serializer::class) public class NodeSyncHook1(override val node: Node, private val serializer1: KSerializer) : SyncHook1(), NodeHook { init { init(serializer1) } - override fun call(context: HookContext, serializedArgs: Array) { require(serializedArgs.size == 1) val (p1) = serializedArgs call { f, _ -> f(context, p1 as? T1) } } + + internal class Serializer(private val serializer1: KSerializer) : NodeWrapperSerializer>({ + NodeSyncHook1(it, serializer1) + }) } +@Serializable(with = NodeSyncHook2.Serializer::class) public class NodeSyncHook2(override val node: Node, private val serializer1: KSerializer, private val serializer2: KSerializer) : SyncHook<(HookContext, T1?, T2?) -> Unit>(), NodeHook { init { init(serializer1, serializer2) } - override fun call(context: HookContext, serializedArgs: Array) { require(serializedArgs.size == 2) val (p1, p2) = serializedArgs @@ -38,10 +43,12 @@ public class NodeSyncHook2(override val node: Node, private val serializ ) } } - public fun tap(name: String, callback: (T1?, T2?) -> Unit): String? = super.tap(name) { _, p1, p2 -> callback(p1, p2) } - public inline fun tap(noinline callback: (T1?, T2?) -> Unit): String? = tap(callingStackTraceElement.toString(), callback) public inline fun tap(noinline callback: (HookContext, T1?, T2?) -> Unit): String? = tap(callingStackTraceElement.toString(), callback) + + internal class Serializer(private val serializer1: KSerializer, private val serializer2: KSerializer) : NodeWrapperSerializer>({ + NodeSyncHook2(it, serializer1, serializer2) + }) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook.kt index 8feeb33b1..6af6e3a2a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHook.kt @@ -3,7 +3,9 @@ package com.intuit.player.jvm.core.bridge.hooks import com.intuit.hooks.HookContext import com.intuit.hooks.SyncWaterfallHook import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable /** * A note of caution when using these hooks. Waterfall hooks act as accumulators, applying the tapped functions @@ -17,6 +19,7 @@ import kotlinx.serialization.KSerializer * outside of the tapped function, this can be done by reading the information and storing it in a non-Node backed * object from within the tapped function. */ +@Serializable(NodeSyncWaterfallHook1.Serializer::class) public class NodeSyncWaterfallHook1(override val node: Node, private val serializer1: KSerializer) : SyncWaterfallHook<(HookContext, T1?) -> T1?, T1?>(), NodeHook { init { init(serializer1) } @@ -27,7 +30,7 @@ public class NodeSyncWaterfallHook1(override val node: Node, private val ser return call( null, { f, _, _ -> f(context, p1) }, - { f, _ -> f(context, p1) } + { f, _ -> f(context, p1) }, ) } @@ -36,12 +39,17 @@ public class NodeSyncWaterfallHook1(override val node: Node, private val ser public inline fun tap(noinline callback: (T1?) -> T1?): String? = tap(callingStackTraceElement.toString(), callback) public inline fun tap(noinline callback: (HookContext, T1?) -> T1?): String? = tap(callingStackTraceElement.toString(), callback) + + internal class Serializer(private val serializer1: KSerializer) : NodeWrapperSerializer>({ + NodeSyncWaterfallHook1(it, serializer1) + }) } +@Serializable(NodeSyncWaterfallHook2.Serializer::class) public class NodeSyncWaterfallHook2( override val node: Node, private val serializer1: KSerializer, - private val serializer2: KSerializer + private val serializer2: KSerializer, ) : SyncWaterfallHook<(HookContext, T1?, T2?) -> T1?, T1?>(), NodeHook { init { init(serializer1, serializer2) } @@ -53,7 +61,7 @@ public class NodeSyncWaterfallHook2( return call( null, { f, _, _ -> f(context, p1, p2) }, - { f, _ -> f(context, p1, p2) } + { f, _ -> f(context, p1, p2) }, ) } @@ -62,4 +70,8 @@ public class NodeSyncWaterfallHook2( public inline fun tap(noinline callback: (T1?, T2?) -> T1?): String? = tap(callingStackTraceElement.toString(), callback) public inline fun tap(noinline callback: (HookContext, T1?, T2?) -> T1?): String? = tap(callingStackTraceElement.toString(), callback) + + internal class Serializer(private val serializer1: KSerializer, private val serializer2: KSerializer) : NodeWrapperSerializer>({ + NodeSyncWaterfallHook2(it, serializer1, serializer2) + }) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeConfig.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeConfig.kt index 48f92d0b9..9032cc359 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeConfig.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeConfig.kt @@ -1,4 +1,9 @@ package com.intuit.player.jvm.core.bridge.runtime +import kotlinx.coroutines.CoroutineExceptionHandler + /** Base configuration for [Runtime] */ -public open class PlayerRuntimeConfig +public open class PlayerRuntimeConfig { + public open var debuggable: Boolean = false + public open var coroutineExceptionHandler: CoroutineExceptionHandler? = null +} diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory.kt index 501359dc7..1612016ec 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/PlayerRuntimeFactory.kt @@ -13,7 +13,7 @@ public interface PlayerRuntimeFactory { * with further configurations from the [nested] block. */ public fun PlayerRuntimeFactory.config( - nested: T.() -> Unit + nested: T.() -> Unit, ): PlayerRuntimeFactory { val parent = this @@ -33,5 +33,5 @@ public val runtimeContainers: List = PlayerRuntimeContai /** Default [PlayerRuntimeFactory] to use if none are specified */ public val runtimeFactory: PlayerRuntimeFactory<*> = runtimeContainers.firstOrNull()?.factory ?: error( "Failed to find JS Player runtime implementation in the classpath: consider adding player runtime dependency. " + - "See https://TODO" + "See https://TODO", ) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/Runtime.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/Runtime.kt index 199309df4..c4f9d458c 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/Runtime.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/runtime/Runtime.kt @@ -4,6 +4,8 @@ import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat import com.intuit.player.jvm.core.bridge.serialization.format.encodeToRuntimeValue import com.intuit.player.jvm.core.bridge.serialization.format.serializer +import com.intuit.player.jvm.core.utils.InternalPlayerApi +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy @@ -11,6 +13,10 @@ import kotlinx.serialization.SerializationStrategy /** Special [Node] that represents the JS runtime */ public interface Runtime : Node { + public val dispatcher: CoroutineDispatcher + + public val config: PlayerRuntimeConfig + /** [CoroutineScope] that represents when the [Runtime] is released and relevant coroutines should cancel */ public val scope: CoroutineScope @@ -19,6 +25,8 @@ public interface Runtime : Node { /** Execute some arbitrary [script] and return the deserialized result */ public fun execute(script: String): Any? + public fun load(scriptContext: ScriptContext): Any? + /** Serialize and assign some [value] to [name] within the [Runtime] */ public fun add(name: String, value: Value) @@ -31,6 +39,10 @@ public interface Runtime : Node { /** Close the [Runtime] and release any resources */ public fun release() + + /** Opportunity to verify the thread to perfom blocking operations on */ + @InternalPlayerApi + public var checkBlockingThread: Thread.() -> Unit } public inline fun Runtime.add(name: String, value: T): Unit = @@ -43,3 +55,8 @@ public inline fun Runtime<*>.serialize(value: T): Any? = public inline fun Runtime<*>.serialize(serializer: SerializationStrategy?, value: T): Any? = serializer?.let { serialize(it, value) } ?: serialize(value) + +public data class ScriptContext( + val script: String, + val id: String, +) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/AbstractRuntimeValueDecoders.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/AbstractRuntimeValueDecoders.kt index bc524af6f..7136cc363 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/AbstractRuntimeValueDecoders.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/AbstractRuntimeValueDecoders.kt @@ -1,12 +1,10 @@ package com.intuit.player.jvm.core.bridge.serialization.encoding -import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeDecodingException import com.intuit.player.jvm.core.bridge.serialization.json.isJsonElementSerializer import com.intuit.player.jvm.core.bridge.serialization.json.value -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializer -import com.intuit.player.jvm.core.bridge.toFunction +import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.core.utils.InternalPlayerApi import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.PolymorphicSerializer @@ -18,6 +16,7 @@ import kotlinx.serialization.json.JsonNull import kotlinx.serialization.modules.SerializersModule import kotlin.reflect.full.isSubclassOf +// TODO: Better support for nullish values /** Common decoder base implementation to support decoding [T] runtime objects */ @InternalPlayerApi public abstract class AbstractRuntimeValueDecoder : RuntimeValueDecoder { @@ -27,16 +26,14 @@ public abstract class AbstractRuntimeValueDecoder : RuntimeValueDecoder { @Suppress("UNCHECKED_CAST") override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = when { deserializer is PolymorphicSerializer<*> -> when { - deserializer.baseClass.isSubclassOf(Function::class) -> decodeFunction().let { - if (deserializer.baseClass == Invokable::class) it else it.toFunction(deserializer.baseClass.simpleName!!) - } + deserializer.baseClass.isSubclassOf(Function::class) -> decodeFunction(GenericSerializer()) deserializer.baseClass.isSubclassOf(Node::class) -> decodeNode() else -> super.decodeSerializableValue(deserializer) } as T // handle json serializers separately because they don't support custom decoders deserializer.isJsonElementSerializer -> when { - decodeNotNullMark() -> Json.encodeToJsonElement(NodeSerializer(), decodeValue() as Node) + decodeNotNullMark() -> Json.encodeToJsonElement(GenericSerializer(), decodeValue()) else -> JsonNull } as T @@ -54,7 +51,6 @@ public abstract class AbstractRuntimeValueDecoder : RuntimeValueDecoder { override fun decodeShort(): Short = decode().toShort() override fun decodeString(): String = decode() override fun decodeNode(): Node = decode() - override fun decodeFunction(): Invokable<*> = decode() override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = enumDescriptor.getElementIndex(decode()) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoders.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoders.kt index 281a62e93..e56281b54 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoders.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/encoding/NodeDecoders.kt @@ -7,6 +7,7 @@ import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeEncodingExc import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat import com.intuit.player.jvm.core.bridge.serialization.json.value import com.intuit.player.jvm.core.utils.InternalPlayerApi +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder @@ -57,7 +58,7 @@ public interface FunctionEncoder : Encoder { } public interface FunctionDecoder : Decoder { - public fun decodeFunction(): Invokable<*> + public fun decodeFunction(returnTypeSerializer: KSerializer): Invokable } @InternalPlayerApi diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat.kt index b33740bc1..d0ba1a2f6 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/format/RuntimeFormat.kt @@ -4,11 +4,16 @@ import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.bridge.serialization.json.PrettyJson import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.core.bridge.serialization.serializers.KCallableSerializer -import kotlinx.serialization.* +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerialFormat +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.StringFormat import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.SerializersModuleBuilder import kotlinx.serialization.modules.contextual import kotlinx.serialization.modules.plus +import kotlinx.serialization.serializer import kotlin.reflect.KCallable import kotlin.reflect.KClass import kotlin.reflect.full.isSubclassOf @@ -58,7 +63,7 @@ public fun RuntimeFormat<*>.registerContextualSerializer(klass: KClass /** [RuntimeFormat] specific [KSerializer] lookup for [T] type that handles special cases before delegating to the [serializersModule] */ public inline fun RuntimeFormat<*>.serializer(): KSerializer = when { T::class == Any::class -> GenericSerializer().conform() - T::class.isSubclassOf(KCallable::class) -> KCallableSerializer() as KSerializer + T::class.isSubclassOf(KCallable::class) -> KCallableSerializer(GenericSerializer().conform()) as KSerializer else -> serializersModule.serializer() } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/json/pretty.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/json/Pretty.kt similarity index 100% rename from jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/json/pretty.kt rename to jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/json/Pretty.kt diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/defer.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/Defer.kt similarity index 100% rename from jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/defer.kt rename to jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/Defer.kt diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionSerializers.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionSerializers.kt index 3eedf724d..dffd1fc6a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionSerializers.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/FunctionSerializers.kt @@ -10,13 +10,16 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.buildSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.modules.SerializersModule import kotlin.reflect.KCallable -public sealed class FunctionLikeSerializer : KSerializer { +public sealed class FunctionLikeSerializer(private val serializer: KSerializer<*>) : KSerializer { final override val descriptor: SerialDescriptor = FunctionLikeSerializer.descriptor - override fun deserialize(decoder: Decoder): T = decoder.requireNodeDecoder().decodeFunction() as T + override fun deserialize(decoder: Decoder): T { + return decoder.requireNodeDecoder().decodeFunction(serializer) as T + } override fun serialize(encoder: Encoder, value: T) { encoder.requireNodeEncoder().encodeFunction(value) @@ -25,13 +28,109 @@ public sealed class FunctionLikeSerializer : KSerializer { public companion object { @OptIn(InternalSerializationApi::class) public val descriptor: SerialDescriptor = buildSerialDescriptor("FunctionLike", PolymorphicKind.OPEN) {} + + public val functionSerializerModule: SerializersModule = SerializersModule { + contextual(KCallable::class) { + KCallableSerializer(it.first()) + } + contextual(Invokable::class) { + InvokableSerializer(it.first()) + } + contextual(Function0::class) { + Function0Serializer(it[0]) + } + contextual(Function1::class) { + Function1Serializer(it[0], it[1]) + } + contextual(Function2::class) { + Function2Serializer(it[0], it[1], it[2]) + } + contextual(Function3::class) { + Function3Serializer(it[0], it[1], it[2], it[3]) + } + contextual(Function4::class) { + Function4Serializer(it[0], it[1], it[2], it[3], it[4]) + } + contextual(Function5::class) { + Function5Serializer(it[0], it[1], it[2], it[3], it[4], it[5]) + } + contextual(Function6::class) { + Function6Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6]) + } + contextual(Function7::class) { + Function7Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7]) + } + contextual(Function8::class) { + Function8Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8]) + } + contextual(Function9::class) { + Function9Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9]) + } + contextual(Function10::class) { + Function10Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10]) + } + contextual(Function11::class) { + Function11serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11]) + } + contextual(Function12::class) { + Function12Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12]) + } + contextual(Function13::class) { + Function13Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13]) + } + contextual(Function14::class) { + Function14Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14]) + } + contextual(Function15::class) { + Function15Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15]) + } + contextual(Function16::class) { + Function16Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15], it[16]) + } + contextual(Function17::class) { + Function17Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15], it[16], it[17]) + } + contextual(Function18::class) { + Function18Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15], it[16], it[17], it[18]) + } + contextual(Function19::class) { + Function19Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15], it[16], it[17], it[18], it[19]) + } + contextual(Function20::class) { + Function20Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15], it[16], it[17], it[18], it[19], it[20]) + } + contextual(Function21::class) { + Function21Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15], it[16], it[17], it[18], it[19], it[20], it[21]) + } + contextual(Function22::class) { + Function22Serializer(it[0], it[1], it[2], it[3], it[4], it[5], it[6], it[7], it[8], it[9], it[10], it[11], it[12], it[13], it[14], it[15], it[16], it[17], it[18], it[19], it[20], it[21], it[22]) + } + } } } -public class KCallableSerializer : FunctionLikeSerializer>() -public class InvokableSerializer : FunctionLikeSerializer>() -public class FunctionSerializer : FunctionLikeSerializer>() - -public inline fun > functionSerializer(): KSerializer = object : KSerializer by FunctionSerializer() as KSerializer { - override val descriptor: SerialDescriptor = SerialDescriptor(T::class.qualifiedName!!, FunctionSerializer().descriptor) -} +public class KCallableSerializer(returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class InvokableSerializer(returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function0Serializer(returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function1Serializer(p1: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function2Serializer(p1: KSerializer<*>, p2: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function3Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function4Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function5Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function6Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function7Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function8Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function9Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function10Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function11serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function12Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function13Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function14Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function15Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function16Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, p16: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function17Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, p16: KSerializer<*>, p17: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function18Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, p16: KSerializer<*>, p17: KSerializer<*>, p18: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function19Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, p16: KSerializer<*>, p17: KSerializer<*>, p18: KSerializer<*>, p19: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function20Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, p16: KSerializer<*>, p17: KSerializer<*>, p18: KSerializer<*>, p19: KSerializer<*>, p20: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function21Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, p16: KSerializer<*>, p17: KSerializer<*>, p18: KSerializer<*>, p19: KSerializer<*>, p20: KSerializer<*>, p21: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) +public class Function22Serializer(p1: KSerializer<*>, p2: KSerializer<*>, p3: KSerializer<*>, p4: KSerializer<*>, p5: KSerializer<*>, p6: KSerializer<*>, p7: KSerializer<*>, p8: KSerializer<*>, p9: KSerializer<*>, p10: KSerializer<*>, p11: KSerializer<*>, p12: KSerializer<*>, p13: KSerializer<*>, p14: KSerializer<*>, p15: KSerializer<*>, p16: KSerializer<*>, p17: KSerializer<*>, p18: KSerializer<*>, p19: KSerializer<*>, p20: KSerializer<*>, p21: KSerializer<*>, p22: KSerializer<*>, returnTypeSerializer: KSerializer) : FunctionLikeSerializer>(returnTypeSerializer) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/GenericSerializer.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/GenericSerializer.kt index 12f3fa10d..511ab5d4b 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/GenericSerializer.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/GenericSerializer.kt @@ -6,7 +6,9 @@ import com.intuit.player.jvm.core.bridge.serialization.encoding.NodeDecoder import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeSerializationException import com.intuit.player.jvm.core.bridge.serialization.json.value import com.intuit.player.jvm.core.utils.InternalPlayerApi -import kotlinx.serialization.* +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.SerializationException import kotlinx.serialization.builtins.ArraySerializer import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.MapSerializer @@ -15,7 +17,12 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -import kotlinx.serialization.json.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.serializerOrNull /** * Generic serializer for [Any]? that uses the actual value type to determine the [KSerializer] @@ -47,12 +54,12 @@ public open class GenericSerializer(private val typeSerializers: List encoder.encodeNull() else -> encoder.encodeSerializableValue( diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/module.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/Module.kt similarity index 61% rename from jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/module.kt rename to jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/Module.kt index 5e0541895..ebc799ab3 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/module.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/Module.kt @@ -1,8 +1,12 @@ package com.intuit.player.jvm.core.bridge.serialization.serializers -import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node -import kotlinx.serialization.modules.* +import com.intuit.player.jvm.core.player.state.ErroneousState +import com.intuit.player.jvm.core.player.state.PlayerFlowState +import kotlinx.serialization.modules.PolymorphicModuleBuilder +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.plus +import kotlinx.serialization.modules.polymorphic public val playerSerializersModule: SerializersModule = SerializersModule { fun PolymorphicModuleBuilder.registerThrowableSerializers() { @@ -19,6 +23,8 @@ public val playerSerializersModule: SerializersModule = SerializersModule { default { NodeSerializer() } } + polymorphic(PlayerFlowState::class, ErroneousState::class, ErroneousState.serializer()) + polymorphic(Any::class) { registerThrowableSerializers() @@ -27,7 +33,4 @@ public val playerSerializersModule: SerializersModule = SerializersModule { contextual(Any::class, ::GenericSerializer) contextual(Throwable::class, ThrowableSerializer()) - contextual(Invokable::class) { - InvokableSerializer() - } -} +} + FunctionLikeSerializer.functionSerializerModule diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableField.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableField.kt index c022b4c36..0a7a6c3a9 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableField.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableField.kt @@ -3,19 +3,121 @@ package com.intuit.player.jvm.core.bridge.serialization.serializers import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper import com.intuit.player.jvm.core.bridge.serialization.format.serializer +import com.intuit.player.jvm.core.experimental.ExperimentalPlayerApi import com.intuit.player.jvm.core.player.PlayerException import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PolymorphicKind +import kotlinx.serialization.descriptors.StructureKind +import kotlinx.serialization.serializer import kotlin.reflect.KProperty -internal class NodeSerializableField(private val provider: () -> Node, private val serializer: KSerializer, private val name: String? = null) { +/** Delegate for automatic deserialization of [Node] values */ +public class NodeSerializableField private constructor( + private val provider: () -> Node, + private val serializer: KSerializer, + internal val strategy: CacheStrategy, + private val name: String?, + private val defaultValue: Node.(String) -> T, +) { - operator fun getValue(thisRef: Any?, property: KProperty<*>): T = provider() - .getSerializable(name ?: property.name, serializer) - ?: throw PlayerException("Could not deserialize $name") + /** Caching strategy for determining how to pull the value from [Node] on subsequent attempts */ + public enum class CacheStrategy { + None, + Smart, + Full, + } + + /** Cache of container [Node] that will reset the [value] cache if out-of-date with the [provider] */ + private var cache: Node = provider(); get() { + val provided = provider() + if (provided.nativeReferenceEquals(field)) { + field + } else { + field = provided + value = null + } + + return field + } + + /** Cache of the [T] value, along with the backing [Node] for objects */ + private var value: Pair? = null + + public operator fun getValue(thisRef: Any?, property: KProperty<*>): T { + // early exit if we have a value and explicitly using the cache + value?.takeIf { strategy == CacheStrategy.Full }?.let { (_, value) -> + return value + } + + val key = name ?: property.name - companion object { - fun NodeWrapper.NodeSerializableField(serializer: KSerializer, name: String? = null) = NodeSerializableField(::node, serializer, name) + // will reset cache and value if mismatch + val node = cache - inline fun NodeWrapper.NodeSerializableField(name: String? = null) = NodeSerializableField(::node, node.format.serializer(), name) + // early exit if we have a value still and referentially match the backing [Node] + value?.takeIf { strategy == CacheStrategy.Smart } + ?.takeIf { (backing) -> backing?.nativeReferenceEquals(node[key]) == true } + ?.let { (_, value) -> return value } + + // else get and deserialize the value + return node + .getSerializable(key, serializer) + ?.also { value = node.getObject(key) to it } + ?: node.defaultValue(key) + } + + public companion object { + + /** Smart constructor responsible for determining the correct [CacheStrategy] and [defaultValue] from the [serializer], if either are not provided */ + @ExperimentalPlayerApi + public operator fun invoke( + provider: () -> Node, + serializer: KSerializer, + strategy: CacheStrategy? = null, + name: String? = null, + defaultValue: (Node.(String) -> T)? = null, + ): NodeSerializableField = NodeSerializableField( + provider, + serializer, + strategy ?: when (serializer.descriptor.kind) { + is StructureKind, + is PolymorphicKind, + -> CacheStrategy.Smart + else -> CacheStrategy.None + }, + name, + defaultValue ?: { + @Suppress("UNCHECKED_CAST") + if (serializer.descriptor.isNullable) { + null as T + } else { + throw throw PlayerException("""Could not deserialize "$it" as "${serializer.descriptor}"""") + } + }, + ) + + /** Smart constructor to automatically determine the [KSerializer] to use for [T] */ + @ExperimentalPlayerApi + public inline operator fun invoke( + noinline provider: () -> Node, + strategy: CacheStrategy? = null, + name: String? = null, + noinline defaultValue: (Node.(String) -> T)? = null, + ): NodeSerializableField = NodeSerializableField(provider, serializer(), strategy, name, defaultValue) } } + +@ExperimentalPlayerApi +public fun NodeWrapper.NodeSerializableField( + serializer: KSerializer, + strategy: NodeSerializableField.CacheStrategy? = null, + name: String? = null, + defaultValue: (Node.(String) -> T)? = null, +): NodeSerializableField = NodeSerializableField(::node, serializer, strategy, name, defaultValue) + +@ExperimentalPlayerApi +public inline fun NodeWrapper.NodeSerializableField( + strategy: NodeSerializableField.CacheStrategy? = null, + name: String? = null, + noinline defaultValue: (Node.(String) -> T)? = null, +): NodeSerializableField = NodeSerializableField(node.format.serializer(), strategy, name, defaultValue) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializers.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializers.kt index c7987bcb1..dbbb374ca 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializers.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializers.kt @@ -29,8 +29,12 @@ public class NodeSerializer : KSerializer { } } -public open class NodeWrapperSerializer(private val factory: (Node) -> T) : KSerializer { - final override val descriptor: SerialDescriptor = buildClassSerialDescriptor("com.intuit.player.jvm.core.bridge.NodeWrapper") +public open class NodeWrapperSerializer( + private val factory: (Node) -> T, + // TODO: Can we pull this from the @SerialName annotation? + private val serialName: String = "com.intuit.player.jvm.core.bridge.NodeWrapper", +) : KSerializer { + final override val descriptor: SerialDescriptor = buildClassSerialDescriptor(serialName) override fun deserialize(decoder: Decoder): T = NodeSerializer().deserialize(decoder).let(factory) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer.kt index 76e561e97..2361d56fc 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/ThrowableSerializer.kt @@ -1,16 +1,25 @@ package com.intuit.player.jvm.core.bridge.serialization.serializers import com.intuit.player.jvm.core.bridge.JSErrorException +import com.intuit.player.jvm.core.bridge.serialization.encoding.NodeDecoder import com.intuit.player.jvm.core.bridge.serialization.encoding.requireNodeDecoder import com.intuit.player.jvm.core.bridge.serialization.encoding.requireNodeEncoder import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.core.utils.InternalPlayerApi -import kotlinx.serialization.* +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.Serializer import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.nullable +import kotlinx.serialization.encoding.CompositeDecoder +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.encoding.decodeStructure +import kotlinx.serialization.encoding.encodeStructure // the serializer knows _how_ to construct the `Throwable` type from a JS Node // and _how_ to deconstruct a `Throwable` into primitives to serialize @@ -25,50 +34,67 @@ public class ThrowableSerializer : KSerializer { element("cause", defer { ThrowableSerializer().descriptor.nullable }, isOptional = true) } - override fun deserialize(decoder: Decoder): PlayerException = decoder.decodeStructure(descriptor) { - var serialized = false - var message = "" - var stackTrace: Array = emptyArray() - var cause: Throwable? = null + override fun deserialize(decoder: Decoder): PlayerException { + // uncaught JVM exceptions can come across as strings or actual exceptions + if (decoder is NodeDecoder) { + when (val value = decoder.decodeValue()) { + is String -> return PlayerException("uncaught exception: $value") + is PlayerException -> return value + is Exception -> return PlayerException("uncaught exception: ${value.message}", value) + } + } - fun decodeStackTraceFromStack(stack: String? = decodeNullableSerializableElement(descriptor, 2, String.serializer().nullable)): Array = stack - ?.split("\n") - ?.mapNotNull(errorStackReg::find) - ?.map(MatchResult::destructured) - ?.map { (className, methodName, fileName, lineNumber) -> - StackTraceElement(className, methodName, fileName, lineNumber.toIntOrNull() ?: -2) - }?.toTypedArray() ?: emptyArray() + return decoder.decodeStructure(descriptor) { + var serialized = false + var message = "" + var stackTrace: Array = emptyArray() + var cause: Throwable? = null - fun decodeSerializedStackTrace(): Array = decodeNullableSerializableElement(descriptor, 3, serializedStackTraceSerializer.nullable) - ?.map { (className, methodName, fileName, lineNumber) -> - StackTraceElement(className, methodName, fileName, lineNumber) - }?.toTypedArray() ?: emptyArray() + fun decodeStackTraceFromStack(stack: String? = decodeNullableSerializableElement(descriptor, 2, String.serializer().nullable)): Array = stack + ?.split("\n") + ?.mapNotNull(errorStackReg::find) + ?.map(MatchResult::destructured) + ?.map { (className, methodName, fileName, lineNumber) -> + StackTraceElement(className, methodName, fileName, lineNumber.toIntOrNull() ?: -2) + }?.toTypedArray() ?: emptyArray() - if (decodeSequentially()) { - serialized = decodeNullableSerializableElement(descriptor, 0, Boolean.serializer().nullable) ?: false + fun decodeSerializedStackTrace(): Array = decodeNullableSerializableElement(descriptor, 3, serializedStackTraceSerializer.nullable) + ?.map { (className, methodName, fileName, lineNumber) -> + StackTraceElement(className, methodName, fileName, lineNumber) + }?.toTypedArray() ?: emptyArray() - if (serialized) { - message = decodeNullableSerializableElement(descriptor, 1, String.serializer().nullable) ?: "" - stackTrace = decodeSerializedStackTrace() - cause = decodeNullableSerializableElement(descriptor, 4, nullable) - } else stackTrace = decodeStackTraceFromStack() - } else while (true) { - when (val index = decodeElementIndex(descriptor)) { - 0 -> serialized = decodeNullableSerializableElement(descriptor, 0, Boolean.serializer().nullable) ?: false - 1 -> message = decodeNullableSerializableElement(descriptor, 1, String.serializer().nullable) ?: "" - 2 -> stackTrace = decodeStackTraceFromStack() - 3 -> stackTrace = decodeSerializedStackTrace() - 4 -> cause = decodeNullableSerializableElement(descriptor, 4, nullable) - CompositeDecoder.DECODE_DONE -> break - else -> error("Unexpected index: $index") + if (decodeSequentially()) { + serialized = decodeNullableSerializableElement(descriptor, 0, Boolean.serializer().nullable) ?: false + + if (serialized) { + message = decodeNullableSerializableElement(descriptor, 1, String.serializer().nullable) ?: "" + stackTrace = decodeSerializedStackTrace() + cause = decodeNullableSerializableElement(descriptor, 4, nullable) + } else { + stackTrace = decodeStackTraceFromStack() + } + } else { + while (true) { + when (val index = decodeElementIndex(descriptor)) { + 0 -> serialized = decodeNullableSerializableElement(descriptor, 0, Boolean.serializer().nullable) ?: false + 1 -> message = decodeNullableSerializableElement(descriptor, 1, String.serializer().nullable) ?: "" + 2 -> stackTrace = decodeStackTraceFromStack() + 3 -> stackTrace = decodeSerializedStackTrace() + 4 -> cause = decodeNullableSerializableElement(descriptor, 4, nullable) + CompositeDecoder.DECODE_DONE -> break + else -> error("Unexpected index: $index") + } + } } - } - if (serialized) PlayerException(message, cause) else { - val error = decoder.requireNodeDecoder().decodeNode() - stackTrace = decodeStackTraceFromStack(error.getString("stack")) - JSErrorException(error) - }.apply { setStackTrace(stackTrace) } + if (serialized) { + PlayerException(message, cause) + } else { + val error = decoder.requireNodeDecoder().decodeNode() + stackTrace = decodeStackTraceFromStack(error.getString("stack")) + JSErrorException(error) + }.apply { setStackTrace(stackTrace) } + } } override fun serialize(encoder: Encoder, value: Throwable) { @@ -79,15 +105,17 @@ public class ThrowableSerializer : KSerializer { encodeNullableSerializableElement(descriptor, 1, String.serializer(), value.message) encodeStringElement(descriptor, 2, value.stackTraceToString()) encodeSerializableElement( - descriptor, 3, serializedStackTraceSerializer, + descriptor, + 3, + serializedStackTraceSerializer, value.stackTrace.map { stackTraceElement: StackTraceElement -> SerializableStackTraceElement( stackTraceElement.className, stackTraceElement.methodName, stackTraceElement.fileName, - stackTraceElement.lineNumber + stackTraceElement.lineNumber, ) - } + }, ) encodeNullableSerializableElement(descriptor, 4, nullable, value.cause) } @@ -106,7 +134,7 @@ public class ThrowableSerializer : KSerializer { public companion object { private val errorStackReg: Regex = """(?<=at )(?[A-z\d\s.<>$]*(?=\.))?\.?(?[A-z\d\s.<>$]*(?= ))? ?\(?(?:.*, )?(?[A-z\d\s.<>$]*)?:?(?[A-z\d\s.<>$]*)?:?(?[A-z\d\s.<>$]*)?\)?$""".toRegex( - RegexOption.MULTILINE + RegexOption.MULTILINE, ) private val serializedStackTraceSerializer = ListSerializer(SerializableStackTraceElement.serializer()) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataController.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataController.kt index 24c50fb6a..150c3c57f 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataController.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataController.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.data import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable @@ -10,15 +11,15 @@ import kotlinx.serialization.Serializable public class DataController internal constructor(override val node: Node) : NodeWrapper { /** Apply [data] to the underlying data model */ public fun set(data: Map) { - node.getFunction("set")?.invoke(data) + node.getInvokable("set")?.invoke(data) } /** [set] each of the [Binding]s contained in the [transaction] */ public fun set(transaction: List>) { - node.getFunction("set")?.invoke(transaction) + node.getInvokable("set")?.invoke(transaction) } - public fun get(binding: Binding): Any? = node.getFunction("get")?.invoke(binding) + public fun get(binding: Binding): Any? = node.getInvokable("get")?.invoke(binding) internal object Serializer : NodeWrapperSerializer(::DataController) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataModelWithParser.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataModelWithParser.kt index c58bff0cf..4838eb7e9 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataModelWithParser.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/data/DataModelWithParser.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.data import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable @@ -12,12 +13,12 @@ import kotlinx.serialization.Serializable public class DataModelWithParser internal constructor(override val node: Node) : NodeWrapper { /** Retrieve specific section of the data model resolved from the [binding] */ public fun get(binding: Binding): Any? { - return node.getFunction("get")?.invoke(binding) + return node.getInvokable("get")?.invoke(binding) } /** [set] each of the [Binding]s contained in the [transaction] */ public fun set(transaction: List>) { - node.getFunction("set")?.invoke(transaction) + node.getInvokable("set")?.invoke(transaction) } internal object Serializer : NodeWrapperSerializer(::DataModelWithParser) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/experimental/ExperimentalPlayerApi.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/experimental/ExperimentalPlayerApi.kt index 625c6b68f..d77d06283 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/experimental/ExperimentalPlayerApi.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/experimental/ExperimentalPlayerApi.kt @@ -11,7 +11,7 @@ package com.intuit.player.jvm.core.experimental AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, - AnnotationTarget.TYPEALIAS + AnnotationTarget.TYPEALIAS, ) @RequiresOptIn("These Player APIs are in active development and may change. Use with caution", RequiresOptIn.Level.WARNING) public annotation class ExperimentalPlayerApi diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/Expression.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/Expression.kt index ca5f5b2b6..54d9ca6e0 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/Expression.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/Expression.kt @@ -1,10 +1,15 @@ package com.intuit.player.jvm.core.expressions +import com.intuit.player.jvm.core.expressions.Expression.Collection +import com.intuit.player.jvm.core.expressions.Expression.Serializer +import com.intuit.player.jvm.core.expressions.Expression.Single import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.descriptors.* +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.listSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder @@ -26,7 +31,7 @@ public sealed class Expression { override val descriptor = PrimitiveSerialDescriptor( Single::class.toString(), - PrimitiveKind.STRING + PrimitiveKind.STRING, ) override fun serialize(encoder: Encoder, value: Single) = @@ -74,7 +79,7 @@ public sealed class Expression { */ override val descriptor = PrimitiveSerialDescriptor( Single::class.toString(), - PrimitiveKind.STRING + PrimitiveKind.STRING, ) override fun serialize(encoder: Encoder, value: Expression) = when (value) { diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/ExpressionEvaluator.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/ExpressionEvaluator.kt index ac684fdf2..962baffd4 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/ExpressionEvaluator.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/expressions/ExpressionEvaluator.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.expressions import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable @@ -30,7 +31,7 @@ public fun ExpressionEvaluator.evaluate(expression: Expression): Any? = when (ex @Serializable(ExpressionController.Serializer::class) public class ExpressionController(override val node: Node) : NodeWrapper, ExpressionEvaluator { override fun evaluate(expressions: List): Any? = node - .getFunction("evaluate")?.invoke(expressions) + .getInvokable("evaluate")?.invoke(expressions) internal object Serializer : NodeWrapperSerializer(::ExpressionController) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Flow.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Flow.kt index 882cc392c..7c6a42f7b 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Flow.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Flow.kt @@ -8,10 +8,10 @@ import kotlinx.serialization.json.JsonNull @Serializable public data class Flow( val id: String = UNKNOWN_ID, - val views: List = emptyList(), + val views: List? = emptyList(), val schema: JsonElement = JsonNull, val data: JsonElement = JsonNull, - val navigation: Navigation? = null + val navigation: Navigation? = null, ) { public companion object { diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowController.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowController.kt index 8ac60abad..94ed3b110 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowController.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowController.kt @@ -2,26 +2,29 @@ package com.intuit.player.jvm.core.flow import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.Companion.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.nullable /** Limited definition of the player flow controller that enables flow transitions */ @Serializable(with = FlowController.Serializer::class) public class FlowController internal constructor(override val node: Node) : NodeWrapper, Transition { override fun transition(state: String, options: TransitionOptions?) { - node.getFunction("transition")?.invoke(state, options) + node.getInvokable("transition")?.invoke(state, options) } public val hooks: Hooks by NodeSerializableField(Hooks.serializer()) - public val current: FlowInstance? get() = node.getSerializable("current", FlowInstance.serializer()) + public val current: FlowInstance? by NodeSerializableField(FlowInstance.serializer().nullable) @Serializable(Hooks.Serializer::class) public class Hooks internal constructor(override val node: Node) : NodeWrapper { + public val flow: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("flow")!!, FlowInstance.serializer()) + by NodeSerializableField(NodeSyncHook1.serializer(FlowInstance.serializer())) internal object Serializer : NodeWrapperSerializer(::Hooks) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowInstance.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowInstance.kt index b48158bd9..ee20d0dff 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowInstance.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowInstance.kt @@ -2,12 +2,14 @@ package com.intuit.player.jvm.core.flow import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.hooks.NodeSyncBailHook1 import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook2 import com.intuit.player.jvm.core.bridge.hooks.NodeSyncWaterfallHook1 import com.intuit.player.jvm.core.bridge.hooks.NodeSyncWaterfallHook2 import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.Companion.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.flow.state.NavigationFlowState import com.intuit.player.jvm.core.player.state.NamedState @@ -18,49 +20,39 @@ import kotlinx.serialization.builtins.serializer @Serializable(FlowInstance.Serializer::class) public class FlowInstance(override val node: Node) : NodeWrapper, Transition { - public val id: String by NodeSerializableField() + public val id: String by NodeSerializableField(String.serializer()) public val hooks: Hooks by NodeSerializableField(Hooks.serializer()) - public val currentState: NamedState? get() = node.getSerializable("currentState", NamedState.serializer()) + public val currentState: NamedState? by NodeSerializableField(NamedState.serializer().nullable) override fun transition(state: String, options: TransitionOptions?) { - node.getFunction("transition")?.invoke(state, options) + node.getInvokable("transition")?.invoke(state, options) } @Serializable(Hooks.Serializer::class) public class Hooks internal constructor(override val node: Node) : NodeWrapper { /** A callback when the onStart node was present */ - public val onStart: NodeSyncHook1 get() = NodeSyncHook1( - node.getObject("onStart")!!, - GenericSerializer() - ) + public val onStart: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(GenericSerializer())) /** A callback when the onEnd node was present */ - public val onEnd: NodeSyncHook1 get() = NodeSyncHook1( - node.getObject("onEnd")!!, - GenericSerializer() - ) + public val onEnd: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(GenericSerializer())) /** A chance to manipulate the flow-node used to calculate the given transition used */ - public val beforeTransition: NodeSyncWaterfallHook2 get() = NodeSyncWaterfallHook2( - node.getObject("beforeTransition")!!, - NavigationFlowState.serializer(), - String.serializer(), - ) + public val beforeTransition: NodeSyncWaterfallHook2 + by NodeSerializableField(NodeSyncWaterfallHook2.serializer(NavigationFlowState.serializer(), String.serializer())) + + /** A hook to intercept and block a transition */ + public val skipTransition: NodeSyncBailHook1 + by NodeSerializableField(NodeSyncBailHook1.serializer(NamedState.serializer(), Boolean.serializer())) /** A chance to manipulate the flow-node calculated after a transition */ - public val resolveTransitionNode: NodeSyncWaterfallHook1 get() = NodeSyncWaterfallHook1( - node.getObject("resolveTransitionNode")!!, - NavigationFlowState.serializer(), - ) + public val resolveTransitionNode: NodeSyncWaterfallHook1 + by NodeSerializableField(NodeSyncWaterfallHook1.serializer(NavigationFlowState.serializer())) /** A callback when a transition from 1 state to another was made */ - public val transition: NodeSyncHook2 get() = NodeSyncHook2( - node.getObject("transition")!!, - NamedState.serializer().nullable, - NamedState.serializer() - ) + public val transition: NodeSyncHook2 + by NodeSerializableField(NodeSyncHook2.serializer(NamedState.serializer().nullable, NamedState.serializer())) internal object Serializer : NodeWrapperSerializer(::Hooks) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowResult.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowResult.kt index 42d8bc839..b528ef19c 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowResult.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/FlowResult.kt @@ -2,11 +2,9 @@ package com.intuit.player.jvm.core.flow import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper -import com.intuit.player.jvm.core.bridge.getJson +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.flow.state.NavigationFlowEndState -import com.intuit.player.jvm.core.flow.state.NavigationFlowState -import com.intuit.player.jvm.core.player.PlayerException import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull @@ -15,12 +13,10 @@ import kotlinx.serialization.json.JsonNull public class FlowResult(override val node: Node) : NodeWrapper { /** End state describing _how_ the flow ended (forwards, backwards, etc) */ - public val endState: NavigationFlowEndState - get() = node.getSerializable("endState", NavigationFlowState.serializer()) - as? NavigationFlowEndState ?: throw PlayerException("flow result not defined") + public val endState: NavigationFlowEndState by NodeSerializableField(NavigationFlowEndState.serializer()) /** The serialized data-model */ - public val data: JsonElement get() = node.getJson("data") ?: JsonNull + public val data: JsonElement by NodeSerializableField(JsonElement.serializer()) { JsonNull } override fun equals(other: Any?): Boolean = node == other diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Navigation.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Navigation.kt index a02dc173f..b0aa6a940 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Navigation.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/Navigation.kt @@ -10,5 +10,5 @@ import kotlinx.serialization.Serializable @Serializable public data class Navigation( val BEGIN: String, - val flows: Map = emptyMap() + val flows: Map = emptyMap(), ) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/NavigationFlow.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/NavigationFlow.kt index 9e6eebd5f..4e6e8d060 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/NavigationFlow.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/NavigationFlow.kt @@ -11,5 +11,5 @@ public data class NavigationFlow( val startState: String, val onStart: Expression?, @Transient - val states: Map? = null + val states: Map? = null, ) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/TransitionOptions.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/TransitionOptions.kt index fc9d1412b..531884a62 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/TransitionOptions.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/TransitionOptions.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable /** Options used to change how transitions are handled */ @Serializable public data class TransitionOptions( - val force: Boolean = false + val force: Boolean = false, ) { public companion object { diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowState.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowState.kt index 575062906..9b6972b72 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowState.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowState.kt @@ -2,42 +2,37 @@ package com.intuit.player.jvm.core.flow.state import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer -import com.intuit.player.jvm.core.bridge.serialization.serializers.PolymorphicNodeWrapperSerializer +import com.intuit.player.jvm.core.bridge.snapshot +import com.intuit.player.jvm.core.experimental.ExperimentalPlayerApi +import com.intuit.player.jvm.core.experimental.RuntimeClassDiscriminator import com.intuit.player.jvm.core.expressions.Expression -import com.intuit.player.jvm.core.flow.state.NavigationFlowStateType.* -import kotlinx.serialization.KSerializer +import com.intuit.player.jvm.core.flow.state.NavigationFlowStateType.ACTION +import com.intuit.player.jvm.core.flow.state.NavigationFlowStateType.END +import com.intuit.player.jvm.core.flow.state.NavigationFlowStateType.EXTERNAL +import com.intuit.player.jvm.core.flow.state.NavigationFlowStateType.FLOW +import com.intuit.player.jvm.core.flow.state.NavigationFlowStateType.VIEW import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.serializer /** The base representation of a state within a Flow */ -@Serializable(with = NavigationFlowState.NavigationFlowStateSerializer::class) +@Serializable +@RuntimeClassDiscriminator("state_type") public sealed class NavigationFlowState : NodeWrapper { /** A property to determine the type of state this is */ public abstract val stateType: NavigationFlowStateType - - internal class NavigationFlowStateSerializer : PolymorphicNodeWrapperSerializer() { - override fun selectDeserializer(node: Node): KSerializer { - return when (NavigationFlowStateType.valueOf(node.getString("state_type") ?: "")) { - VIEW -> NavigationFlowViewState.serializer() - END -> NavigationFlowEndState.serializer() - FLOW -> NavigationFlowFlowState.serializer() - ACTION -> NavigationFlowActionState.serializer() - EXTERNAL -> NavigationFlowExternalState.serializer() - } - } - } } /** A generic state that can transition to another state */ +@Serializable public sealed class NavigationFlowTransitionableState(override val node: Node) : NavigationFlowState() { /** A mapping of transition-name to FlowState name */ - public val transitions: Map - get() = node.getObject("transitions")?.run { - keys.map { it to (this.getString(it) ?: "") }.toMap() - } ?: emptyMap() + public val transitions: Map by NodeSerializableField(MapSerializer(String.serializer(), String.serializer())) /** An id corresponding to a view from the 'views' array */ - public val ref: String get() = node.getString("ref") ?: "" + public val ref: String by NodeSerializableField(String.serializer()) } /** Action states execute an expression to determine the next state to transition to */ @@ -52,9 +47,9 @@ public class NavigationFlowActionState internal constructor(override val node: N * An expression to execute. * The return value determines the transition to take */ - public val exp: Expression get() = node.getSerializable("exp", Expression.serializer())!! + public val exp: Expression by NodeSerializableField(Expression.serializer()) - internal object Serializer : NodeWrapperSerializer(::NavigationFlowActionState) + internal object Serializer : NodeWrapperSerializer(::NavigationFlowActionState, ACTION.name) } /** An END state of the flow */ @@ -62,7 +57,7 @@ public class NavigationFlowActionState internal constructor(override val node: N public class NavigationFlowEndState internal constructor(override val node: Node) : NavigationFlowState(), NodeWrapper, - Map by node { + Map by node.snapshot() { override val stateType: NavigationFlowStateType = END @@ -70,11 +65,14 @@ public class NavigationFlowEndState internal constructor(override val node: Node * A description of _how_ the flow ended. * If this is a flow started from another flow, the outcome determines the flow transition */ - public val outcome: String get() = node.getString("outcome") ?: "" + public val outcome: String by NodeSerializableField(String.serializer()) { "" } - internal object Serializer : NodeWrapperSerializer(::NavigationFlowEndState) + internal object Serializer : NodeWrapperSerializer(::NavigationFlowEndState, END.name) } +@ExperimentalPlayerApi +public val NavigationFlowEndState.param: Any? get() = get("param") + /** * External Flow states represent states in the FSM that can't be resolved internally in the player. * The flow will wait for the embedded application to manage moving to the next state via a transition @@ -89,7 +87,7 @@ public class NavigationFlowExternalState internal constructor(override val node: /** Getter for any additional properties */ public operator fun get(name: String): Any? = node[name] - internal object Serializer : NodeWrapperSerializer(::NavigationFlowExternalState) + internal object Serializer : NodeWrapperSerializer(::NavigationFlowExternalState, EXTERNAL.name) } @Serializable(with = NavigationFlowFlowState.Serializer::class) @@ -99,7 +97,7 @@ public class NavigationFlowFlowState internal constructor(override val node: Nod override val stateType: NavigationFlowStateType = FLOW - internal object Serializer : NodeWrapperSerializer(::NavigationFlowFlowState) + internal object Serializer : NodeWrapperSerializer(::NavigationFlowFlowState, FLOW.name) } /** A state representing a view */ @@ -110,5 +108,5 @@ public class NavigationFlowViewState internal constructor(override val node: Nod override val stateType: NavigationFlowStateType = VIEW - internal object Serializer : NodeWrapperSerializer(::NavigationFlowViewState) + internal object Serializer : NodeWrapperSerializer(::NavigationFlowViewState, VIEW.name) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowStateType.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowStateType.kt index 30ba915bc..d0c529ff5 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowStateType.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/flow/state/NavigationFlowStateType.kt @@ -6,5 +6,5 @@ public enum class NavigationFlowStateType { END, FLOW, ACTION, - EXTERNAL + EXTERNAL, } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/logger/TapableLogger.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/logger/TapableLogger.kt index 4c91662b4..0ad7016b2 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/logger/TapableLogger.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/logger/TapableLogger.kt @@ -3,12 +3,15 @@ package com.intuit.player.jvm.core.logger import com.intuit.hooks.HookContext import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 import com.intuit.player.jvm.core.bridge.hooks.SyncHook1 +import com.intuit.player.jvm.core.bridge.runtime import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.Companion.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.plugins.LoggerPlugin +import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer @@ -25,7 +28,7 @@ public class TapableLogger(override val node: Node) : LoggerPlugin, NodeWrapper private fun loggerHook(key: String) = NodeSyncHook1( node.getObject(key)!!, - ListSerializer(GenericSerializer()) + ListSerializer(GenericSerializer()), ).toTypedArrayHook() internal object Serializer : NodeWrapperSerializer(::Hooks) @@ -34,23 +37,33 @@ public class TapableLogger(override val node: Node) : LoggerPlugin, NodeWrapper public val hooks: Hooks by NodeSerializableField(Hooks.serializer()) public override fun trace(vararg args: Any?) { - node.getFunction("trace")!!(*args) + runtime.scope.launch { + node.getInvokable("trace")!!(*args) + } } public override fun debug(vararg args: Any?) { - node.getFunction("debug")!!(*args) + runtime.scope.launch { + node.getInvokable("debug")!!(*args) + } } public override fun info(vararg args: Any?) { - node.getFunction("info")!!(*args) + runtime.scope.launch { + node.getInvokable("info")!!(*args) + } } public override fun warn(vararg args: Any?) { - node.getFunction("warn")!!(*args) + runtime.scope.launch { + node.getInvokable("warn")!!(*args) + } } public override fun error(vararg args: Any?) { - node.getFunction("error")!!(*args) + runtime.scope.launch { + node.getInvokable("error")!!(*args) + } } public fun addHandler(logger: LoggerPlugin) { diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManager.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManager.kt index 0945b2b0f..5b3312461 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManager.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManager.kt @@ -1,8 +1,13 @@ package com.intuit.player.jvm.core.managed +import com.intuit.player.jvm.core.managed.AsyncIterationManager.State.NotStarted import com.intuit.player.jvm.core.player.state.CompletedState -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch /** Wrapper of an [AsyncIterator] that captures iterations within a [StateFlow] */ public class AsyncIterationManager(public val iterator: AsyncIterator) { @@ -15,7 +20,7 @@ public class AsyncIterationManager(public val iterator public class Error(public val error: Exception) : State() } - private val _state = MutableStateFlow(State.NotStarted) + private val _state = MutableStateFlow(NotStarted) /** a stateful flow capturing each iteration of the [iterator] and maintaining the last update */ public val state: StateFlow = _state.asStateFlow() @@ -30,7 +35,7 @@ public class AsyncIterationManager(public val iterator next?.let(State::Item) ?: State.Done } catch (exception: Exception) { State.Error(exception) - } + }, ) } } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterator.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterator.kt index 6a03bd965..45d991628 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterator.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/managed/AsyncIterator.kt @@ -1,10 +1,8 @@ package com.intuit.player.jvm.core.managed import com.intuit.player.jvm.core.player.state.CompletedState -import kotlinx.coroutines.ExperimentalCoroutinesApi /** Generic async iterator that uses some [Result] to determine the next [Item] in the iteration */ -@OptIn(ExperimentalCoroutinesApi::class) public interface AsyncIterator { /** diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayer.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayer.kt index f2664e4e3..742d6f92d 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayer.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayer.kt @@ -3,17 +3,29 @@ package com.intuit.player.jvm.core.player import com.intuit.player.jvm.core.bridge.Completable import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeConfig import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.runtime.add import com.intuit.player.jvm.core.bridge.runtime.runtimeFactory -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.Companion.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField +import com.intuit.player.jvm.core.experimental.ExperimentalPlayerApi import com.intuit.player.jvm.core.logger.TapableLogger import com.intuit.player.jvm.core.player.HeadlessPlayer.Companion.bundledSource import com.intuit.player.jvm.core.player.state.CompletedState import com.intuit.player.jvm.core.player.state.PlayerFlowState import com.intuit.player.jvm.core.player.state.ReleasedState -import com.intuit.player.jvm.core.plugins.* +import com.intuit.player.jvm.core.player.state.inProgressState +import com.intuit.player.jvm.core.plugins.JSPluginWrapper +import com.intuit.player.jvm.core.plugins.LoggerPlugin +import com.intuit.player.jvm.core.plugins.PlayerPlugin +import com.intuit.player.jvm.core.plugins.Plugin +import com.intuit.player.jvm.core.plugins.RuntimePlugin import com.intuit.player.jvm.core.plugins.logging.loggers +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import java.net.URL /** @@ -31,60 +43,106 @@ import java.net.URL * - [JSPluginWrapper] * - [PlayerPlugin] */ -public class HeadlessPlayer public constructor( +public class HeadlessPlayer +@ExperimentalPlayerApi +@JvmOverloads +public constructor( override val plugins: List, - public val runtime: Runtime<*> = runtimeFactory.create(), + explicitRuntime: Runtime<*>? = null, private val source: URL = bundledSource, + config: PlayerRuntimeConfig = PlayerRuntimeConfig(), ) : Player(), NodeWrapper { /** Convenience constructor to allow [plugins] to be passed as varargs */ - @JvmOverloads public constructor( + @ExperimentalPlayerApi @JvmOverloads + public constructor( vararg plugins: Plugin, - runtime: Runtime<*> = runtimeFactory.create(), - source: URL = bundledSource - ) : this(plugins.toList(), runtime, source) + config: PlayerRuntimeConfig = PlayerRuntimeConfig(), + explicitRuntime: Runtime<*>? = null, + source: URL = bundledSource, + ) : this(plugins.toList(), explicitRuntime, source, config) + + public constructor( + explicitRuntime: Runtime<*>, + vararg plugins: Plugin, + ) : this(plugins.toList(), explicitRuntime, config = explicitRuntime.config) private val player: Node override val node: Node by ::player - override val logger: TapableLogger by NodeSerializableField(TapableLogger.serializer()) + override val logger: TapableLogger by NodeSerializableField(TapableLogger.serializer(), NodeSerializableField.CacheStrategy.Full) - override val hooks: Hooks by NodeSerializableField(Hooks.serializer()) + override val hooks: Hooks by NodeSerializableField(Hooks.serializer(), NodeSerializableField.CacheStrategy.Full) + + override val state: PlayerFlowState get() = if (player.isReleased()) { + ReleasedState + } else { + player.getInvokable("getState")!!().deserialize(PlayerFlowState.serializer()) + } + + public val runtime: Runtime<*> = explicitRuntime ?: runtimeFactory.create { + debuggable = config.debuggable + coroutineExceptionHandler = config.coroutineExceptionHandler ?: CoroutineExceptionHandler { _, throwable -> + inProgressState?.fail(throwable) ?: logger.error( + "Exception caught in Player scope: ${throwable.message}", + throwable.stackTrace.joinToString("\n") { + "\tat $it" + }.replaceFirst("\tat ", "\n"), + ) + } + } - override val state: PlayerFlowState get() = if (player.isReleased()) ReleasedState else - player.getFunction("getState")!!().deserialize(PlayerFlowState.serializer()) + override val scope: CoroutineScope by runtime::scope init { /** 1. load source into the [runtime] and release lock */ - runtime.execute(source.readText()) + runtime.load(ScriptContext(if (runtime.config.debuggable) debugSource.readText() else source.readText(), bundledSourcePath)) - /** 2. apply JS plugins and build [JSPlayerConfig] */ + /** 2. merge explicit [LoggerPlugin]s with ones created by service loader */ + val loggerPlugins = plugins.filterIsInstance().let { explicitLoggers -> + val explicitLoggerPlugins = explicitLoggers.map { it::class } + explicitLoggers + loggers { name = this@HeadlessPlayer::class.java.name } + .filterNot { explicitLoggerPlugins.contains(it::class) } + } + + /** 3. apply runtime plugins and build [JSPlayerConfig] */ val config = plugins .filterIsInstance() .onEach { it.apply(runtime) } .filterIsInstance() - .let(::JSPlayerConfig) + .let { JSPlayerConfig(it, loggerPlugins) } - /** 3. Build JS headless player */ + /** 4. build JS headless player */ runtime.add("config", config) player = runtime.execute("new Player.Player(config)") as? Node ?: throw PlayerException("Couldn't create backing JS Player w/ config: $config") - /** 4. apply to player plugins */ + runtime.add("player", player) + + // we only have access to the logger after we have the player instance + runtime.checkBlockingThread = { + if (name == "main") { + scope.launch { + logger.warn( + "Main thread is blocking on JS runtime access: $this", + stackTrace.joinToString("\n") { + "\tat $it" + }.replaceFirst("\tat ", "\n"), + ) + } + } + } + + /** 5. apply to player plugins */ plugins .filterIsInstance() .onEach { it.apply(this) } - - /** 5. apply logger plugins */ - loggers - .filterNot { plugins.map { it::class }.contains(it::class) } - .forEach(logger::addHandler) } override fun start(flow: String): Completable = start(runtime.execute("($flow)") as Node) - public fun start(flow: Node): Completable = PlayerCompletable(player.getFunction("start")!!.invoke(flow)) + public fun start(flow: Node): Completable = PlayerCompletable(player.getInvokable("start")!!.invoke(flow)) /** Start a [flow] and subscribe to the result */ public fun start(flow: Node, onComplete: (Result) -> Unit): Completable = @@ -93,8 +151,8 @@ public class HeadlessPlayer public constructor( } override fun release() { - // TODO: Call state hook! if (!runtime.isReleased()) { + hooks.state.call(HashMap(), arrayOf(ReleasedState)) runtime.release() } } @@ -102,9 +160,13 @@ public class HeadlessPlayer public constructor( internal companion object { private const val bundledSourcePath = "core/player/dist/player.prod.js" + private const val debugSourcePath = "core/player/dist/player.dev.js" /** Gets [URL] of the bundled source */ private val bundledSource get() = this::class.java .classLoader.getResource(bundledSourcePath)!! + + private val debugSource get() = this::class.java + .classLoader.getResource(debugSourcePath)!! } } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/JSPlayerConfig.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/JSPlayerConfig.kt index 0cae7081c..5a19517d2 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/JSPlayerConfig.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/JSPlayerConfig.kt @@ -1,10 +1,22 @@ package com.intuit.player.jvm.core.player +import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.plugins.JSPluginWrapper +import com.intuit.player.jvm.core.plugins.LoggerPlugin import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient -/** Expected structure of JS object used to instantiate the core JS player */ +/** [JSPluginWrapper]s and [LoggerPlugin]s to configure a JS Player with */ @Serializable public data class JSPlayerConfig( val plugins: List = listOf(), -) + @Transient val loggers: List = emptyList(), +) { + val logger: Map> = mapOf( + "trace" to Invokable { args -> loggers.forEach { it.trace(*args) } }, + "debug" to Invokable { args -> loggers.forEach { it.debug(*args) } }, + "info" to Invokable { args -> loggers.forEach { it.info(*args) } }, + "warn" to Invokable { args -> loggers.forEach { it.warn(*args) } }, + "error" to Invokable { args -> loggers.forEach { it.error(*args) } }, + ) +} diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/Player.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/Player.kt index c748430f4..6c495c888 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/Player.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/Player.kt @@ -4,6 +4,7 @@ import com.intuit.player.jvm.core.bridge.Completable import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.data.DataController import com.intuit.player.jvm.core.experimental.ExperimentalPlayerApi @@ -11,14 +12,21 @@ import com.intuit.player.jvm.core.expressions.ExpressionController import com.intuit.player.jvm.core.flow.FlowController import com.intuit.player.jvm.core.flow.FlowResult import com.intuit.player.jvm.core.logger.TapableLogger -import com.intuit.player.jvm.core.player.state.* +import com.intuit.player.jvm.core.player.state.CompletedState +import com.intuit.player.jvm.core.player.state.PlayerFlowState +import com.intuit.player.jvm.core.player.state.ReleasedState import com.intuit.player.jvm.core.plugins.Pluggable import com.intuit.player.jvm.core.validation.ValidationController import com.intuit.player.jvm.core.view.View import com.intuit.player.jvm.core.view.ViewController +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.job import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import java.net.URL +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** Agnostic [Pluggable] [Player] to provide the core API */ public abstract class Player : Pluggable { @@ -34,7 +42,7 @@ public abstract class Player : Pluggable { /** [Player] hooks that expose underlying controllers for supplementing functionality */ @Serializable(Hooks.Companion.Serializer::class) public interface Hooks { - /** The hook that fires every time we create a new flowController (a new flow is started) */ + /** The hook that fires every time we create a new flowController (a new FRF) */ public val flowController: NodeSyncHook1 /** The hook that updates/handles view things */ @@ -57,22 +65,15 @@ public abstract class Player : Pluggable { public companion object { internal interface HooksByNode : Hooks, NodeWrapper public fun serializer(): KSerializer = Serializer - internal operator fun invoke(node: Node): HooksByNode = object : HooksByNode { + internal operator fun invoke(node: Node): HooksByNode = object : HooksByNode, NodeWrapper { override val node: Node = node - override val flowController: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("flowController")!!, FlowController.serializer()) - override val viewController: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("viewController")!!, ViewController.serializer()) - override val view: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("view")!!, View.serializer()) - override val expressionEvaluator: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("expressionEvaluator")!!, ExpressionController.serializer()) - override val dataController: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("dataController")!!, DataController.serializer()) - override val validationController: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("validationController")!!, ValidationController.serializer()) - override val state: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("state")!!, PlayerFlowState.serializer()) + override val flowController: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(FlowController.serializer())) + override val viewController: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(ViewController.serializer())) + override val view: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(View.serializer())) + override val expressionEvaluator: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(ExpressionController.serializer())) + override val dataController: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(DataController.serializer())) + override val validationController: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(ValidationController.serializer())) + override val state: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(PlayerFlowState.serializer())) } internal object Serializer : KSerializer by NodeWrapperSerializer(Hooks::invoke) as KSerializer @@ -82,6 +83,9 @@ public abstract class Player : Pluggable { /** The current [PlayerFlowState] of the player. */ public abstract val state: PlayerFlowState + /** [CoroutineScope] to be used for launching coroutines that should be cancelled once the Player is released */ + public abstract val scope: CoroutineScope + /** * Asynchronously [start] the [flow] represented as a [String]. The * [FlowResult] can be obtained by subscribing the the [Completable.onComplete] @@ -121,3 +125,13 @@ public abstract class Player : Pluggable { onComplete(onComplete) } } + +/** + * Create a child [CoroutineScope] of the [Player.scope], such that it'll inherit the [CoroutineContext], + * but with its own [SupervisorJob]. Unless overridden by the provided [context], this ensures that the + * sub-scope can be cancelled independently, but still contains other top-level elements, such as the + * dispatcher or coroutine exception handler. + */ +@ExperimentalPlayerApi +public fun Player.subScope(context: CoroutineContext = EmptyCoroutineContext): CoroutineScope = + CoroutineScope(scope.coroutineContext + SupervisorJob(scope.coroutineContext.job) + context) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerCompletable.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerCompletable.kt index 28935152a..59a725cde 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerCompletable.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerCompletable.kt @@ -3,8 +3,9 @@ package com.intuit.player.jvm.core.player import com.intuit.player.jvm.core.bridge.Completable import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.Promise -import com.intuit.player.jvm.core.bridge.toCompletable -import com.intuit.player.jvm.core.player.state.* +import com.intuit.player.jvm.core.player.state.CompletedState +import com.intuit.player.jvm.core.player.state.ErrorState +import com.intuit.player.jvm.core.player.state.PlayerFlowState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerFlowStatus.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerFlowStatus.kt index d49098848..e1502f220 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerFlowStatus.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerFlowStatus.kt @@ -6,17 +6,7 @@ public enum class PlayerFlowStatus(public val value: String) { IN_PROGRESS("in-progress"), COMPLETED("completed"), ERROR("error"), - /** Only a JVM player state */ - RELEASED("released"); - public companion object { - /** Flexibly and safely get the corresponding [PlayerFlowStatus] from the [value] */ - public fun from(value: Any?): PlayerFlowStatus = when (value) { - NOT_STARTED.value -> NOT_STARTED - IN_PROGRESS.value -> IN_PROGRESS - COMPLETED.value -> COMPLETED - RELEASED.value -> RELEASED - else -> ERROR - } - } + /** Only a JVM player state */ + RELEASED("released"), } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerHooks.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerHooks.kt index 35d3090ef..238739534 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerHooks.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/PlayerHooks.kt @@ -3,6 +3,6 @@ package com.intuit.player.jvm.core.player @Deprecated( "Abstracted by Player.Hooks interface, the old implementation of Node-backed hooks is within Player.Hooks.Companion as an anonymous object", ReplaceWith("Player.Hooks"), - DeprecationLevel.ERROR + DeprecationLevel.ERROR, ) public typealias PlayerHooks = Player.Hooks diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/NamedState.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/NamedState.kt index 8a054347f..d8d82ca5a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/NamedState.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/NamedState.kt @@ -2,19 +2,19 @@ package com.intuit.player.jvm.core.player.state import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.flow.state.NavigationFlowState import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.serializer @Serializable(NamedState.Serializer::class) public class NamedState internal constructor(override val node: Node) : NodeWrapper { /** The name of the navigation node */ - public val name: String - get() = node.getString("name") ?: "" + public val name: String by NodeSerializableField(String.serializer()) /** The navigation node */ - public val value: NavigationFlowState - get() = node.getSerializable("value", NavigationFlowState.serializer())!! + public val value: NavigationFlowState by NodeSerializableField(NavigationFlowState.serializer()) override fun toString(): String = (name to value).toString() diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/PlayerFlowState.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/PlayerFlowState.kt index 3aca2407a..72c86562a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/PlayerFlowState.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/player/state/PlayerFlowState.kt @@ -1,12 +1,20 @@ package com.intuit.player.jvm.core.player.state import com.intuit.player.jvm.core.asset.Asset -import com.intuit.player.jvm.core.bridge.* -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.Companion.NodeSerializableField +import com.intuit.player.jvm.core.bridge.Completable +import com.intuit.player.jvm.core.bridge.EmptyNode +import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.Promise +import com.intuit.player.jvm.core.bridge.deserialize +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.getSerializable +import com.intuit.player.jvm.core.bridge.getSymbol +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer -import com.intuit.player.jvm.core.bridge.serialization.serializers.PolymorphicNodeWrapperSerializer import com.intuit.player.jvm.core.data.DataController import com.intuit.player.jvm.core.data.DataModelWithParser +import com.intuit.player.jvm.core.experimental.RuntimeClassDiscriminator import com.intuit.player.jvm.core.expressions.ExpressionController import com.intuit.player.jvm.core.expressions.ExpressionEvaluator import com.intuit.player.jvm.core.flow.Flow @@ -14,39 +22,30 @@ import com.intuit.player.jvm.core.flow.FlowController import com.intuit.player.jvm.core.flow.FlowResult import com.intuit.player.jvm.core.flow.Transition import com.intuit.player.jvm.core.flow.state.NavigationFlowEndState -import com.intuit.player.jvm.core.flow.state.NavigationFlowState import com.intuit.player.jvm.core.player.Player import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.core.player.PlayerFlowStatus -import com.intuit.player.jvm.core.player.PlayerFlowStatus.* +import com.intuit.player.jvm.core.player.PlayerFlowStatus.COMPLETED +import com.intuit.player.jvm.core.player.PlayerFlowStatus.ERROR +import com.intuit.player.jvm.core.player.PlayerFlowStatus.IN_PROGRESS +import com.intuit.player.jvm.core.player.PlayerFlowStatus.NOT_STARTED +import com.intuit.player.jvm.core.player.PlayerFlowStatus.RELEASED import com.intuit.player.jvm.core.validation.ValidationController import com.intuit.player.jvm.core.view.View import com.intuit.player.jvm.core.view.ViewController -import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull /** The base representation of the player state */ -@Serializable(with = PlayerFlowStateSerializer::class) +@Serializable +@RuntimeClassDiscriminator("status") public sealed class PlayerFlowState : NodeWrapper { /** The status of the given flow */ public abstract val status: PlayerFlowStatus /** A unique reference for the life-cycle of a flow */ - public val ref: String? get() = node.getString("ref") -} - -internal class PlayerFlowStateSerializer : PolymorphicNodeWrapperSerializer() { - override fun selectDeserializer(node: Node): KSerializer { - return when (PlayerFlowStatus.from(node.getString("status"))) { - NOT_STARTED -> NotStartedState.serializer() - IN_PROGRESS -> InProgressState.serializer() - COMPLETED -> CompletedState.serializer() - ERROR -> ErroneousState.serializer() - RELEASED -> ReleasedState.ReleasedStateSerializer - } - } + public val ref: String? get() = node.getSymbol("ref") } /** Player state describing when a flow completed successfully */ @@ -56,15 +55,14 @@ public class CompletedState(override val node: Node) : override val status: PlayerFlowStatus = COMPLETED - public val endState: NavigationFlowEndState - get() = node.getSerializable("endState", NavigationFlowState.serializer()) - as? NavigationFlowEndState ?: throw PlayerException("flow result not defined") + public val endState: NavigationFlowEndState by NodeSerializableField(NavigationFlowEndState.serializer()) - public val data: JsonElement get() = node.getJson("data") ?: JsonNull + public val data: JsonElement by NodeSerializableField(JsonElement.serializer()) { JsonNull } - public val dataModel: DataModelWithParser get() = node.getSerializable("dataModel")!! + // TODO: Completed state dataModel change needs rectification here + public val dataModel: DataModelWithParser by NodeSerializableField(DataModelWithParser.serializer()) - internal object Serializer : NodeWrapperSerializer(::CompletedState) + internal object Serializer : NodeWrapperSerializer(::CompletedState, COMPLETED.value) } /** Player state describing when a flow finished but not successfully */ @@ -97,17 +95,20 @@ internal class ErroneousState(override val node: Node) : ErrorState(), NodeWrapper { - override val flow: Flow get() = node.getSerializable("flow", Flow.serializer()) ?: Flow() + override val flow: Flow by NodeSerializableField(Flow.serializer()) { Flow() } - override val error: PlayerException get() = when (val rawError = node["error"]) { - is PlayerException -> rawError - is Exception -> PlayerException(rawError.message ?: "", rawError) - is Node -> rawError.deserialize() - is String -> PlayerException(rawError) - else -> PlayerException("unable to determine error") + override val error: PlayerException by NodeSerializableField(PlayerException.serializer()) { + // TODO: Need to test this error handling strategy + when (val rawError = get(it)) { + is PlayerException -> rawError + is Exception -> PlayerException(rawError.message ?: "", rawError) + is Node -> rawError.deserialize() + is String -> PlayerException(rawError) + else -> PlayerException(rawError?.toString() ?: "unable to determine error") + } } - internal object Serializer : NodeWrapperSerializer(::ErroneousState) + internal object Serializer : NodeWrapperSerializer(::ErroneousState, ERROR.value) } /** [InProgressState] is for when a flow is currently executing */ @@ -123,16 +124,16 @@ public class InProgressState internal constructor(override val node: Node) : /** [FlowResult] value that will be available once the flow completes */ // TODO: Make non-nullable if possible - requires Promise change public val flowResult: Completable get() = Promise( - node.getObject("flowResult")!! + node.getObject("flowResult")!!, ).toCompletable(FlowResult.serializer()) public val controllers: ControllerState by NodeSerializableField(ControllerState.serializer()) public fun fail(error: Throwable) { - node.getFunction("fail")!!.invoke(error) + node.getInvokable("fail")!!.invoke(error) } - internal object Serializer : NodeWrapperSerializer(::InProgressState) + internal object Serializer : NodeWrapperSerializer(::InProgressState, IN_PROGRESS.value) } // inline on purpose to capture stack trace of calling site @@ -177,16 +178,17 @@ public class NotStartedState internal constructor(override val node: Node) : override val status: PlayerFlowStatus = NOT_STARTED - internal object Serializer : NodeWrapperSerializer(::NotStartedState) + internal object Serializer : NodeWrapperSerializer(::NotStartedState, NOT_STARTED.value) } /** Terminal player state the signifies when the player resources have been released */ +@Serializable(with = ReleasedState.ReleasedStateSerializer::class) public object ReleasedState : PlayerFlowState(), NodeWrapper { override val node: Node = EmptyNode override val status: PlayerFlowStatus = RELEASED - internal object ReleasedStateSerializer : NodeWrapperSerializer({ ReleasedState }) + internal object ReleasedStateSerializer : NodeWrapperSerializer({ ReleasedState }, RELEASED.value) } /** @@ -198,7 +200,7 @@ public sealed class PlayerFlowExecutionState(override val node: Node) : NodeWrapper { /** The currently executing flow */ - public val flow: Flow get() = node.getSerializable("flow", Flow.serializer()) ?: Flow() + public val flow: Flow by NodeSerializableField(Flow.serializer()) { Flow() } } // Set of *safe* convenience helpers for bounding state to concrete class diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSPluginWrapper.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSPluginWrapper.kt index 29cd491c1..0f5c07bef 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSPluginWrapper.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSPluginWrapper.kt @@ -23,6 +23,6 @@ public interface JSPluginWrapper : RuntimePlugin, NodeWrapper { @Deprecated( "Replaced with more generic JSPluginWrapper", ReplaceWith("JSPluginWrapper"), - DeprecationLevel.HIDDEN + DeprecationLevel.HIDDEN, ) public typealias JSPlayerPluginWrapper = JSPluginWrapper diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSScriptPluginWrapper.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSScriptPluginWrapper.kt index 7cd03c621..4de6945f0 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSScriptPluginWrapper.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/JSScriptPluginWrapper.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.plugins import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext /** * Convenience construct to instantiate a JS player plugin. By default, this will @@ -10,17 +11,22 @@ import com.intuit.player.jvm.core.bridge.runtime.Runtime * can be passed in directly as a [String], or can be passed as a classpath location * and be read from the provided [ClassLoader]. */ -public abstract class JSScriptPluginWrapper(public val name: String, protected val script: String) : JSPluginWrapper { +public abstract class JSScriptPluginWrapper(public val name: String, protected val script: String, private val sourcePath: String? = null) : JSPluginWrapper { public constructor(name: String, sourcePath: String, classLoader: ClassLoader = JSScriptPluginWrapper::class.java.classLoader) : - this(name, classLoader.getResource(sourcePath)!!.readText()) + this(name, classLoader.getResource(sourcePath)!!.readText(), sourcePath) final override lateinit var instance: Node protected set + protected val debugScript: String + get() = loadDebugScript() ?: script + + private fun loadDebugScript(): String? = JSScriptPluginWrapper::class.java.classLoader.getResource(sourcePath?.substringBeforeLast(".") + ".dev." + sourcePath?.substringAfterLast("."))?.readText() + public val isInstantiated: Boolean get() = ::instance.isInitialized override fun apply(runtime: Runtime<*>) { - runtime.execute(script) + runtime.load(ScriptContext(if (runtime.config.debuggable) debugScript else script, sourcePath ?: "$name.js")) instance = runtime.buildInstance() } @@ -32,7 +38,7 @@ public abstract class JSScriptPluginWrapper(public val name: String, protected v public fun from(name: String, sourcePath: String, classLoader: ClassLoader = JSScriptPluginWrapper::class.java.classLoader): JSScriptPluginWrapper = object : JSScriptPluginWrapper(name, sourcePath, classLoader) {} /** Convenience helper to expose constructor as an anonymous builder */ - public fun from(name: String, script: String): JSScriptPluginWrapper = object : JSScriptPluginWrapper(name, script) {} + public fun from(name: String, script: String): JSScriptPluginWrapper = object : JSScriptPluginWrapper(name, script = script) {} } } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/LoggerPlugin.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/LoggerPlugin.kt index 8287d65fc..fdd66ca58 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/LoggerPlugin.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/LoggerPlugin.kt @@ -1,7 +1,6 @@ package com.intuit.player.jvm.core.plugins import com.intuit.player.jvm.core.player.Player -import kotlinx.serialization.encoding.* /** Player logger interface */ public interface LoggerPlugin : PlayerPlugin { @@ -11,9 +10,7 @@ public interface LoggerPlugin : PlayerPlugin { public fun warn(vararg args: Any?) public fun error(vararg args: Any?) - override fun apply(player: Player) { - player.logger.addHandler(this) - } + override fun apply(player: Player) {} } /** Convenience getter to find the first [LoggerPlugin] registered to the [Pluggable] */ diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/RuntimePlugin.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/RuntimePlugin.kt index 9f3d4802e..a063073fa 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/RuntimePlugin.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/RuntimePlugin.kt @@ -6,5 +6,6 @@ import com.intuit.player.jvm.core.bridge.runtime.Runtime public interface RuntimePlugin : Plugin { /** Invoked with the [Runtime] instance to configure */ + // TODO: Should be suspend? public fun apply(runtime: Runtime<*>) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingConfig.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingConfig.kt index 71d9a5d67..5f2fad24c 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingConfig.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingConfig.kt @@ -1,4 +1,6 @@ package com.intuit.player.jvm.core.plugins.logging /** Base configuration for [Logging] */ -public open class PlayerLoggingConfig +public abstract class PlayerLoggingConfig { + public abstract var name: String +} diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory.kt index 5bc5f377b..3a520427a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/plugins/logging/PlayerLoggingFactory.kt @@ -1,7 +1,7 @@ package com.intuit.player.jvm.core.plugins.logging import com.intuit.player.jvm.core.plugins.LoggerPlugin -import java.util.ServiceLoader +import java.util.* /** Factory of [Logging] with a specific [T] of [PlayerLoggingConfig] */ public interface PlayerLoggingFactory { @@ -14,7 +14,7 @@ public interface PlayerLoggingFactory { * with further configurations from the [nested] block. */ public fun PlayerLoggingFactory.config( - nested: T.() -> Unit + nested: T.() -> Unit, ): PlayerLoggingFactory { val parent = this @@ -33,3 +33,6 @@ public val loggerContainers: List = PlayerLoggingContain /** Default [PlayerLoggingFactory] to use if none are specified */ public val loggers: List = loggerContainers.map { it.factory.create() } + +/** Instantiate and configure all [LoggerPlugin]s */ +public fun loggers(block: PlayerLoggingConfig.() -> Unit): List = loggerContainers.map { it.factory.create(block) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/ResolveOptions.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/ResolveOptions.kt index e24380b71..cff975407 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/ResolveOptions.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/ResolveOptions.kt @@ -2,14 +2,16 @@ package com.intuit.player.jvm.core.resolver import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.validation.Validation import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.nullable @Serializable(with = ResolveOptions.Serializer::class) public class ResolveOptions(override val node: Node) : NodeWrapper { - public val validation: Validation? get() = node.getSerializable("validation", Validation.serializer()) + public val validation: Validation? by NodeSerializableField(Validation.serializer().nullable) internal object Serializer : NodeWrapperSerializer(::ResolveOptions) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/Resolver.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/Resolver.kt index a89d53f3c..51eb02e59 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/Resolver.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/resolver/Resolver.kt @@ -3,8 +3,7 @@ package com.intuit.player.jvm.core.resolver import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper import com.intuit.player.jvm.core.bridge.hooks.NodeSyncWaterfallHook2 -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.Companion.NodeSerializableField -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializer +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable @@ -16,11 +15,7 @@ public class Resolver(override val node: Node) : NodeWrapper { @Serializable(with = Hooks.Serializer::class) public class Hooks internal constructor(override val node: Node) : NodeWrapper { public val resolveOptions: NodeSyncWaterfallHook2 - get() = NodeSyncWaterfallHook2( - node.getObject("resolveOptions")!!, - ResolveOptions.serializer(), - NodeSerializer() - ) + by NodeSerializableField(NodeSyncWaterfallHook2.serializer(ResolveOptions.serializer(), Node.serializer())) internal object Serializer : NodeWrapperSerializer(::Hooks) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/utils/Future.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/utils/Future.kt new file mode 100644 index 000000000..95a53e353 --- /dev/null +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/utils/Future.kt @@ -0,0 +1,13 @@ +package com.intuit.player.jvm.core.utils + +import kotlinx.coroutines.delay +import java.util.concurrent.Future + +/** + * Method to await on a Java Future in a coroutine context and returning the result + */ +@InternalPlayerApi +public suspend fun Future.await(): T { + while (!isDone) delay(10) + return get() +} diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/BindingInstance.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/BindingInstance.kt index 0c7d1f2e1..8e3162368 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/BindingInstance.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/BindingInstance.kt @@ -2,15 +2,16 @@ package com.intuit.player.jvm.core.validation import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable @Serializable(with = BindingInstance.Serializer::class) public class BindingInstance(override val node: Node) : NodeWrapper { - public fun asString(): String = node.getFunction("asString")!!() + public fun asString(): String = node.getInvokable("asString")!!() - public fun parent(): BindingInstance? = node.getFunction("parent")?.invoke() + public fun parent(): BindingInstance? = node.getInvokable("parent")?.invoke() override fun toString(): String = asString() diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/Validation.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/Validation.kt index d297781db..5e48871e9 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/Validation.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/Validation.kt @@ -2,7 +2,7 @@ package com.intuit.player.jvm.core.validation import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper -import com.intuit.player.jvm.core.bridge.deserialize +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.resolver.Resolver import kotlinx.serialization.Serializable @@ -11,7 +11,7 @@ import kotlinx.serialization.Serializable @Serializable(with = Validation.Serializer::class) public class Validation internal constructor(override val node: Node) : NodeWrapper { /** get all outstanding validations on current flow */ - public fun getAll(): ValidationMapping? = node.getFunction("getAll")?.invoke()?.deserialize() + public fun getAll(): ValidationMapping? = node.getInvokable("getAll")!!() internal object Serializer : NodeWrapperSerializer(::Validation) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationController.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationController.kt index 7ecbc156d..0723a9ad3 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationController.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationController.kt @@ -2,10 +2,8 @@ package com.intuit.player.jvm.core.validation import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper -import com.intuit.player.jvm.core.bridge.deserialize +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer -import com.intuit.player.jvm.core.data.get -import com.intuit.player.jvm.core.data.set import kotlinx.serialization.Serializable /** Limited definition of the player validationController to enable validating the current view */ @@ -14,7 +12,7 @@ public class ValidationController internal constructor(override val node: Node) /** Get information on whether transition is allowed along with potential blocking validations */ public fun validateView(): ValidationInfo = - node.getFunction("validateView")!!.invoke().deserialize() + node.getInvokable("validateView")!!() internal object Serializer : NodeWrapperSerializer(::ValidationController) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationInfo.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationInfo.kt index acb22927f..46117c81e 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationInfo.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationInfo.kt @@ -8,5 +8,5 @@ public typealias ValidationMapping = JSMap @Serializable public data class ValidationInfo( val canTransition: Boolean, - val validations: ValidationMapping? = null + val validations: ValidationMapping? = null, ) diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationResponse.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationResponse.kt index fd23af5f7..76e64230d 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationResponse.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/validation/ValidationResponse.kt @@ -2,20 +2,24 @@ package com.intuit.player.jvm.core.validation import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper -import com.intuit.player.jvm.core.bridge.deserialize +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer -import com.intuit.player.jvm.core.bridge.serialization.serializers.PolymorphicNodeWrapperSerializer -import com.intuit.player.jvm.core.player.PlayerException -import kotlinx.serialization.KSerializer +import com.intuit.player.jvm.core.experimental.RuntimeClassDiscriminator import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.MapSerializer +import kotlinx.serialization.builtins.nullable +import kotlinx.serialization.builtins.serializer -@Serializable(with = ValidationResponseSerializer::class) +@Serializable +@RuntimeClassDiscriminator("severity") public sealed class ValidationResponse : NodeWrapper { /** The validation message to show to the user */ - public val message: String get() = node.getString("message")!! + public val message: String by NodeSerializableField(String.serializer()) /** List of parameters associated with a validation. */ - public val parameters: Map? get() = node.getObject("parameters")?.deserialize() + public val parameters: Map? by NodeSerializableField(MapSerializer(String.serializer(), GenericSerializer()).nullable) } @Serializable(with = WarningValidationResponse.Serializer::class) @@ -23,23 +27,13 @@ public class WarningValidationResponse(override val node: Node) : ValidationResp /** Warning validations can be dismissed without correcting the error */ public fun dismiss() { - node.getFunction("dismiss")?.invoke() + node.getInvokable("dismiss")?.invoke() } - internal object Serializer : NodeWrapperSerializer(::WarningValidationResponse) + internal object Serializer : NodeWrapperSerializer(::WarningValidationResponse, "warning") } @Serializable(with = ErrorValidationResponse.Serializer::class) public class ErrorValidationResponse(override val node: Node) : ValidationResponse() { - internal object Serializer : NodeWrapperSerializer(::ErrorValidationResponse) -} - -internal class ValidationResponseSerializer : PolymorphicNodeWrapperSerializer() { - override fun selectDeserializer(node: Node): KSerializer { - return when (node.getString("severity")) { - "warning" -> WarningValidationResponse.serializer() - "error" -> ErrorValidationResponse.serializer() - else -> throw PlayerException("ValidationResponse must be Error or Warning") - } - } + internal object Serializer : NodeWrapperSerializer(::ErrorValidationResponse, "error") } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/View.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/View.kt index 4a5efa194..4d8d255ef 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/View.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/View.kt @@ -3,15 +3,17 @@ package com.intuit.player.jvm.core.view import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.nullable /** Limited definition of a stateful view instance from a flow */ @Serializable(with = View.Serializer::class) public class View internal constructor(override val node: Node) : NodeWrapper { - public val hooks: ViewHooks get() = ViewHooks(node.getObject("hooks")!!) + public val hooks: ViewHooks by NodeSerializableField(ViewHooks.serializer()) - public val lastUpdate: Asset? get() = node.getObject("lastUpdate") as? Asset + public val lastUpdate: Asset? by NodeSerializableField(Asset.serializer().nullable) internal object Serializer : NodeWrapperSerializer(::View) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewController.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewController.kt index 1cfcecb70..f539fad36 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewController.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewController.kt @@ -3,9 +3,10 @@ package com.intuit.player.jvm.core.view import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 -import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.Companion.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.nullable /** Limited definition of the player view controller responsible for managing updating/switching views */ @Serializable(with = ViewController.Serializer::class) @@ -13,14 +14,12 @@ public class ViewController internal constructor(override val node: Node) : Node public val hooks: Hooks by NodeSerializableField(Hooks.serializer()) /** Current view of the flow, if any */ - public val currentView: View? get() = - node.getSerializable("currentView", View.serializer()) + public val currentView: View? by NodeSerializableField(View.serializer().nullable) @Serializable(with = Hooks.Serializer::class) public class Hooks internal constructor(override val node: Node) : NodeWrapper { /** The hook right before the View starts resolving. Attach anything custom here */ - public val view: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("view")!!, View.serializer()) + public val view: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(View.serializer())) internal object Serializer : NodeWrapperSerializer(::Hooks) } diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooks.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooks.kt index b2e4c6a2e..24584026c 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooks.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooks.kt @@ -3,6 +3,6 @@ package com.intuit.player.jvm.core.view @Deprecated( "Replaced with ViewController.Hooks", ReplaceWith("ViewController.Hooks"), - DeprecationLevel.WARNING + DeprecationLevel.WARNING, ) public typealias ViewControllerHooks = ViewController.Hooks diff --git a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewHooks.kt b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewHooks.kt index e477de2d0..a037c6d1a 100644 --- a/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewHooks.kt +++ b/jvm/core/src/main/kotlin/com/intuit/player/jvm/core/view/ViewHooks.kt @@ -4,11 +4,15 @@ import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.resolver.Resolver +import kotlinx.serialization.Serializable +@Serializable(with = ViewHooks.Serializer::class) public class ViewHooks internal constructor(override val node: Node) : NodeWrapper { - public val onUpdate: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("onUpdate")!!, Asset.serializer()) - public val resolver: NodeSyncHook1 - get() = NodeSyncHook1(node.getObject("resolver")!!, Resolver.serializer()) + public val onUpdate: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(Asset.serializer())) + public val resolver: NodeSyncHook1 by NodeSerializableField(NodeSyncHook1.serializer(Resolver.serializer())) + + internal object Serializer : NodeWrapperSerializer(::ViewHooks) } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/asset/AssetTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/asset/AssetTest.kt index 64da4ae77..a638769e4 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/asset/AssetTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/asset/AssetTest.kt @@ -5,6 +5,7 @@ import com.intuit.player.jvm.core.player.PlayerException import io.mockk.every import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test internal class AssetTest : NodeBaseTest() { @@ -18,6 +19,14 @@ internal class AssetTest : NodeBaseTest() { Asset(node) } + @BeforeEach + fun setup() { + every { node.nativeReferenceEquals(any()) } returns false + every { node.getObject(any()) } returns node + every { node.getSerializable("id", any()) } returns ID + every { node.getSerializable("type", any()) } returns TYPE + } + @Test fun testId() { every { node.getString("id") } returns ID @@ -32,9 +41,6 @@ internal class AssetTest : NodeBaseTest() { @Test fun testDestructuring() { - every { node.getString("id") } returns ID - every { node.getString("type") } returns TYPE - val (id, type) = asset assertEquals(ID, id) assertEquals(TYPE, type) @@ -43,7 +49,7 @@ internal class AssetTest : NodeBaseTest() { @Test fun testIdNotPresent() { assertThrows(PlayerException::class.java) { - every { node.getString("id") } returns null + every { node.getSerializable("id", any()) } returns null asset.id } } @@ -51,7 +57,7 @@ internal class AssetTest : NodeBaseTest() { @Test fun testTypeNotPresent() { assertThrows(PlayerException::class.java) { - every { node.getString("type") } returns null + every { node.getSerializable("type", any()) } returns null asset.type } } @@ -59,8 +65,8 @@ internal class AssetTest : NodeBaseTest() { @Test fun testDestructuringNotPresent() { assertThrows(PlayerException::class.java) { - every { node.getString("id") } returns null - every { node.getString("type") } returns null + every { node.getSerializable("id", any()) } returns null + every { node.getSerializable("type", any()) } returns null val (id, type) = asset } } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/JsonPromiseTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/JsonPromiseTest.kt index 20904cba4..4304b5281 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/JsonPromiseTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/JsonPromiseTest.kt @@ -2,7 +2,15 @@ package com.intuit.player.jvm.core.bridge import com.intuit.player.jvm.utils.test.RuntimeTest import com.intuit.player.jvm.utils.test.runBlockingTest -import kotlinx.serialization.json.* +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.add +import kotlinx.serialization.json.addJsonArray +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.TestTemplate @@ -55,7 +63,7 @@ internal class JsonPromiseTest : RuntimeTest() { buildJsonObject { put("a", "b") }, - result + result, ) } @@ -72,10 +80,10 @@ internal class JsonPromiseTest : RuntimeTest() { "c", buildJsonObject { put("d", "e") - } + }, ) }, - result + result, ) } @@ -91,7 +99,7 @@ internal class JsonPromiseTest : RuntimeTest() { add((2 as Number)) add((3 as Number)) }, - result + result, ) } @@ -111,7 +119,7 @@ internal class JsonPromiseTest : RuntimeTest() { add((5 as Number)) } }, - result + result, ) } } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/NodeGetSymbolTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/NodeGetSymbolTest.kt new file mode 100644 index 000000000..48b821d93 --- /dev/null +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/NodeGetSymbolTest.kt @@ -0,0 +1,64 @@ +package com.intuit.player.jvm.core.bridge + +import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.utils.test.RuntimeTest +import io.mockk.every +import io.mockk.mockk +import io.mockk.verifySequence +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestTemplate + +class NodeGetSymbolTest : RuntimeTest() { + + @Test + fun `getSymbol injects helper`() { + val runtime: Runtime<*> = mockk() + val node: Node = mockk() + + every { node.runtime } returns runtime + every { runtime.containsKey("getSymbol") } returns false + every { runtime.execute(any()) } returns Unit + every { runtime.getInvokable("getSymbol") } returns Invokable { "Symbol(world)" } + + val symbol = node.getSymbol("hello") + assertEquals("Symbol(world)", symbol) + + verifySequence { + runtime.containsKey("getSymbol") + runtime.execute(any()) + runtime.getInvokable("getSymbol") + } + } + + @Test + fun `getSymbol does not inject helper`() { + val runtime: Runtime<*> = mockk() + val node: Node = mockk() + + every { node.runtime } returns runtime + every { runtime.containsKey("getSymbol") } returns true + every { runtime.getInvokable("getSymbol") } returns Invokable { "Symbol(world)" } + + val symbol = node.getSymbol("hello") + assertEquals("Symbol(world)", symbol) + + verifySequence { + runtime.containsKey("getSymbol") + runtime.getInvokable("getSymbol") + } + } + + @TestTemplate fun `getSymbol works with symbols`() { + val wrapper = runtime.execute("({ ref: Symbol('hello') })") as Node + assertNull(wrapper["ref"]) + assertEquals("Symbol(hello)", wrapper.getSymbol("ref")) + } + + @TestTemplate fun `getSymbol doesn't blow up with non-symbols`() { + val wrapper = runtime.execute("({ ref: 'not a symbol' })") as Node + assertEquals("not a symbol", wrapper["ref"]) + assertNull(wrapper.getSymbol("ref")) + } +} diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/PromiseTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/PromiseTest.kt index 3850439cb..0e72a0a09 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/PromiseTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/PromiseTest.kt @@ -5,8 +5,11 @@ import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.core.player.PlayerFlowStatus import com.intuit.player.jvm.core.player.state.CompletedState import com.intuit.player.jvm.core.player.state.PlayerFlowState +import com.intuit.player.jvm.utils.normalizeStackTraceElements import com.intuit.player.jvm.utils.test.PromiseUtils import com.intuit.player.jvm.utils.test.RuntimeTest +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put @@ -18,8 +21,17 @@ private inline fun currentStackTrace() = Exception().stackTrace internal class PromiseTest : RuntimeTest(), PromiseUtils { - override val thenChain = mutableListOf() - override val catchChain = mutableListOf() + override val thenChain = mutableListOf(); get() = runBlocking { + // TODO: Rework delay into a proper suspension mechanism + delay(100) + field + } + + override val catchChain = mutableListOf(); get() = runBlocking { + // TODO: Rework delay into a proper suspension mechanism + delay(100) + field + } /** * Anti-test case since old test existed to prove the Json conversions forced Ints as Longs. @@ -70,13 +82,13 @@ internal class PromiseTest : RuntimeTest(), PromiseUtils { "flowResult", buildJsonObject { put("outcome", "doneWithTopic") - } + }, ) put( "flow", buildJsonObject { put("id", "some-id") - } + }, ) } val flow = CompletedState( @@ -84,13 +96,13 @@ internal class PromiseTest : RuntimeTest(), PromiseUtils { mapOf( "status" to PlayerFlowStatus.COMPLETED.value, "flowResult" to mapOf( - "outcome" to "doneWithTopic" + "outcome" to "doneWithTopic", ), "flow" to mapOf( - "id" to "some-id" - ) - ) - ) as Node + "id" to "some-id", + ), + ), + ) as Node, ) runtime.Promise.resolve(42) @@ -116,7 +128,7 @@ internal class PromiseTest : RuntimeTest(), PromiseUtils { const promise = new Promise(function(resolve, reject) { resolver = resolve }); return [promise, resolver]; })(); - """.trimIndent() + """.trimIndent(), ) as List<*> Promise(promise as Node) @@ -170,7 +182,7 @@ internal class PromiseTest : RuntimeTest(), PromiseUtils { assertTrue(caught is Throwable) caught as Throwable assertEquals(exception.message, caught.message) - assertEquals(exception.stackTrace.toList(), caught.stackTrace.toList()) + assertEquals(exception.stackTrace.normalizeStackTraceElements(), caught.stackTrace.normalizeStackTraceElements()) } @TestTemplate @@ -188,7 +200,7 @@ internal class PromiseTest : RuntimeTest(), PromiseUtils { caught as Throwable caught.printStackTrace() assertEquals(exception.message, caught.message) - assertEquals(exception.stackTrace.toList(), caught.stackTrace.toList()) + assertEquals(exception.stackTrace.normalizeStackTraceElements(), caught.stackTrace.normalizeStackTraceElements()) } @TestTemplate @@ -205,6 +217,6 @@ internal class PromiseTest : RuntimeTest(), PromiseUtils { assertTrue(caught is Throwable) caught as Throwable assertEquals(exception.message, caught.message) - assertEquals(exception.stackTrace.toList(), caught.stackTrace.toList()) + assertEquals(exception.stackTrace.normalizeStackTraceElements(), caught.stackTrace.normalizeStackTraceElements()) } } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/global/JSMapTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/global/JSMapTest.kt index 40b77f8ba..e9ae59b26 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/global/JSMapTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/global/JSMapTest.kt @@ -2,8 +2,9 @@ package com.intuit.player.jvm.core.bridge.global import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.deserialize +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.utils.test.RuntimeTest -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.TestTemplate @kotlinx.serialization.Serializable @@ -28,10 +29,10 @@ internal class JSMapTest : RuntimeTest() { return { generateMap: () => map }; })() - """.trimIndent() + """.trimIndent(), ) as Node - val map: JSMap = node.getFunction("generateMap")!!().deserialize() + val map: JSMap = node.getInvokable("generateMap")!!().deserialize() assertEquals(1, map.size) assertEquals(setOf(Key(1)), map.keys) assertEquals(listOf(Value(2)), map.values) @@ -50,10 +51,10 @@ internal class JSMapTest : RuntimeTest() { return { generateMap: () => map }; })() - """.trimIndent() + """.trimIndent(), ) as Node - val map: JSMap = node.getFunction("generateMap")!!().deserialize() + val map: JSMap = node.getInvokable("generateMap")!!().deserialize() assertEquals(1, map.size) assertEquals(setOf("hello"), map.keys) assertEquals(listOf(1), map.values) diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHookTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHookTest.kt new file mode 100644 index 000000000..7b8e75fb1 --- /dev/null +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncBailHookTest.kt @@ -0,0 +1,105 @@ +package com.intuit.player.jvm.core.bridge.hooks + +import com.intuit.hooks.BailResult +import com.intuit.player.jvm.core.NodeBaseTest +import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.view.View +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.verify +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class NodeSyncBailHookTest : NodeBaseTest() { + @MockK + private lateinit var dummyNode: Node + + @MockK + private lateinit var invokable: Invokable + + private lateinit var map: HashMap + private lateinit var callback: Invokable + + @Suppress("UNCHECKED_CAST") + @BeforeEach + fun setUpMock() { + val callback = Invokable { + map = it[0] as HashMap + callback = it[1] as Invokable + } + every { node.getInvokable("tap") } returns callback + every { node.getInvokable("tap", any()) } returns callback + every { invokable.invoke(*anyVararg()) } + every { dummyNode.deserialize(View.Serializer) } returns View(dummyNode) + } + + @Test + fun `JS Hook Tap On Init`() { + NodeSyncBailHook1(node, View.serializer()) + verify { node.getInvokable("tap", any()) } + } + + @Test + fun `Hook Tap`() { + var output: View? = null + + val nodeHook = NodeSyncBailHook1(node, View.serializer()) + nodeHook.tap { view -> + output = view + BailResult.Continue() + } + + val result = callback.invoke(hashMapOf(), dummyNode) + Assertions.assertTrue(result == Unit) + Assertions.assertEquals(dummyNode, output?.node) + } + + @Test + fun `Tap with Context`() { + var context: HashMap? = null + + val nodeHook = NodeSyncBailHook1(node, View.serializer()) + nodeHook.tap { map, _ -> + context = map + BailResult.Continue() + } + + callback.invoke(hashMapOf("first" to 2), dummyNode) + + Assertions.assertEquals(hashMapOf("first" to 2), context) + } + + @Test + fun `Tap with Bail result, subsequent taps not called`() { + var secondTapCalled = false + val nodeHook = NodeSyncBailHook1(node, View.serializer()) + nodeHook.tap { _ -> + BailResult.Bail(1) + } + nodeHook.tap { _ -> + secondTapCalled = true + BailResult.Continue() + } + val result = callback.invoke(hashMapOf(), dummyNode) + Assertions.assertTrue(result == 1) + Assertions.assertFalse(secondTapCalled) + } + + @Test + fun `Tap with Continue result, subsequent taps called`() { + var secondTapCalled = false + val nodeHook = NodeSyncBailHook1(node, View.serializer()) + nodeHook.tap { _ -> + BailResult.Continue() + } + nodeHook.tap { _ -> + secondTapCalled = true + BailResult.Continue() + } + callback.invoke(hashMapOf(), dummyNode) + Assertions.assertTrue(secondTapCalled) + } +} diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHookTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHookTest.kt index f386fd45d..0e773fa0c 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHookTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncHookTest.kt @@ -3,6 +3,7 @@ package com.intuit.player.jvm.core.bridge.hooks import com.intuit.player.jvm.core.NodeBaseTest import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.view.View import io.mockk.every import io.mockk.impl.annotations.MockK @@ -14,6 +15,7 @@ import org.junit.jupiter.api.Test internal class NodeSyncHookTest : NodeBaseTest() { @MockK private lateinit var dummyNode: Node + @MockK private lateinit var invokable: Invokable @@ -23,10 +25,12 @@ internal class NodeSyncHookTest : NodeBaseTest() { @Suppress("UNCHECKED_CAST") @BeforeEach fun setUpMock() { - every { node.getFunction("tap") } returns Invokable { + val callback = Invokable { map = it[0] as HashMap callback = it[1] as Invokable } + every { node.getInvokable("tap") } returns callback + every { node.getInvokable("tap", any()) } returns callback every { invokable.invoke(*anyVararg()) } every { dummyNode.deserialize(View.Serializer) } returns View(dummyNode) } @@ -34,7 +38,7 @@ internal class NodeSyncHookTest : NodeBaseTest() { @Test fun `JS Hook Tap On Init`() { NodeSyncHook1(node, View.serializer()) - verify { node.getFunction("tap") } + verify { node.getInvokable("tap", any()) } } @Test @@ -46,7 +50,7 @@ internal class NodeSyncHookTest : NodeBaseTest() { callback.invoke(hashMapOf(), dummyNode) - verify { node.getFunction("tap") } + verify { node.getInvokable("tap", any()) } assertEquals(dummyNode, output?.node) } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHookTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHookTest.kt index e264ba626..d3239a497 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHookTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/hooks/NodeSyncWaterfallHookTest.kt @@ -3,6 +3,7 @@ package com.intuit.player.jvm.core.bridge.hooks import com.intuit.player.jvm.core.NodeBaseTest import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.core.view.View import io.mockk.every @@ -17,6 +18,7 @@ import org.junit.jupiter.api.Test internal class NodeSyncWaterfallHookTest : NodeBaseTest() { @MockK private lateinit var dummyNode: Node + @MockK private lateinit var invokable: Invokable @@ -28,10 +30,12 @@ internal class NodeSyncWaterfallHookTest : NodeBaseTest() { @Suppress("UNCHECKED_CAST") @BeforeEach fun setUpMock() { - every { node.getFunction("tap") } returns Invokable { + val callback = Invokable { map = it[0] as HashMap callback = it[1] as Invokable } + every { node.getInvokable("tap") } returns callback + every { node.getInvokable("tap", any()) } returns callback every { invokable.invoke(*anyVararg()) } every { dummyNode.deserialize(View.Serializer) } returns View(dummyNode) every { dummyNode.deserialize(mapSerializer) } returns mapOf("key" to "value") @@ -40,7 +44,7 @@ internal class NodeSyncWaterfallHookTest : NodeBaseTest() { @Test fun `JS Hook Tap On Init`() { NodeSyncWaterfallHook1(node, View.serializer()) - verify { node.getFunction("tap") } + verify { node.getInvokable("tap", any()) } } @Test @@ -55,7 +59,7 @@ internal class NodeSyncWaterfallHookTest : NodeBaseTest() { callback.invoke(hashMapOf(), dummyNode) - verify { node.getFunction("tap") } + verify { node.getInvokable("tap", any()) } assertEquals(dummyNode, output?.node) } @@ -68,7 +72,7 @@ internal class NodeSyncWaterfallHookTest : NodeBaseTest() { val result = callback.invoke(hashMapOf(), dummyNode) - verify { node.getFunction("tap") } + verify { node.getInvokable("tap", any()) } assertEquals(mapOf("key" to "value", "anotherKey" to "anotherValue"), result) } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/runtime/RuntimeBlockingTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/runtime/RuntimeBlockingTest.kt new file mode 100644 index 000000000..e43fd3471 --- /dev/null +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/runtime/RuntimeBlockingTest.kt @@ -0,0 +1,150 @@ +package com.intuit.player.jvm.core.bridge.runtime + +import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.Promise +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField +import com.intuit.player.jvm.core.bridge.toCompletable +import com.intuit.player.jvm.utils.test.RuntimeTest +import com.intuit.player.jvm.utils.test.runBlockingTest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.suspendCancellableCoroutine +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.TestTemplate +import org.junit.jupiter.api.assertThrows +import java.util.* +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume + +internal class RuntimeBlockingTest : RuntimeTest() { + + private lateinit var handles: HashMap> + private lateinit var jobs: HashMap> + private val blockOnJvm by NodeSerializableField<(key: String) -> Unit>(::runtime) + private val promiseOnJvm by NodeSerializableField<(key: String) -> Node>(::runtime) + + private suspend fun Deferred.assertHandled(handle: Continuation) { + handle.resume(Unit) + await() + Assertions.assertTrue(isCompleted) + } + + private fun CoroutineScope.doOnJvmAsync(key: String, block: suspend Deferred.(Continuation) -> Unit, job: suspend () -> Unit): Pair, Deferred>> = async { job() }.also { jobs[key] = it } to async { + while (handles[key] == null) delay(50) + (handles[key] ?: error("handle shouldn't be null")) + .also { (jobs[key] ?: error("job shouldn't be null")).block(it) } + } + + private fun CoroutineScope.blockOnJvmAsync(key: String = UUID.randomUUID().toString(), block: suspend Deferred.(Continuation) -> Unit = {}): Pair, Deferred>> = + doOnJvmAsync(key, block) { blockOnJvm(key) } + + private fun CoroutineScope.promiseOnJvmAsync(key: String = UUID.randomUUID().toString(), block: suspend Deferred.(Continuation) -> Unit = {}): Pair, Deferred>> = + doOnJvmAsync(key, block) { promiseOnJvm(key).let(::Promise).toCompletable().await() ?: Unit } + + private suspend fun suspendOnHandler(key: String) = suspendCancellableCoroutine { continuation -> + synchronized(handles) { + handles[key] = continuation + } + } + + @BeforeEach fun setup() { + handles = hashMapOf() + jobs = hashMapOf() + runtime.add("blockOnJvm") { key: String -> runBlocking { suspendOnHandler(key) } } + runtime.add("promiseOnJvm") { key: String -> + runtime.Promise { resolve, _ -> + suspendOnHandler(key) + resolve(Unit) + } + } + } + + @TestTemplate fun `block during JS execution`() = runBlockingTest { + val (job, handle) = blockOnJvmAsync { assertHandled(it) } + awaitAll(job, handle) + } + + @TestTemplate fun `suspend during JS execution`() = runBlockingTest { + val (job, handle) = promiseOnJvmAsync { assertHandled(it) } + awaitAll(job, handle) + Unit + } + + @TestTemplate fun `multiple blocks during JS execution`() = runBlockingTest { + suspend fun waitForAllJobsToSuspend(num: Int) { + while (jobs.size < num) delay(50) + } + + val (job1, handle1) = blockOnJvmAsync { + waitForAllJobsToSuspend(2) + assertHandled(it) + } + val (job2, handle2) = blockOnJvmAsync { + waitForAllJobsToSuspend(2) + assertHandled(it) + } + + awaitAll(handle1, handle2, job1, job2) + Unit + } + + @TestTemplate fun `multiple suspends during JS execution`() = runBlockingTest { + suspend fun waitForAllJobsToSuspend(num: Int) { + while (jobs.size < num) delay(50) + } + + val (job1, handle1) = promiseOnJvmAsync { + waitForAllJobsToSuspend(2) + assertHandled(it) + } + val (job2, handle2) = promiseOnJvmAsync { + waitForAllJobsToSuspend(2) + assertHandled(it) + } + + awaitAll(handle1, handle2, job1, job2) + Unit + } + + @TestTemplate fun `blocking during JS execution handled gracefully when releasing`() = runBlockingTest { + // This behavior is only really supported by j2v8 w/ a dedicated runtime + if (runtime.toString() != "J2V8") return@runBlockingTest + + val (job, handle) = blockOnJvmAsync() + handle.await() + + val releaseJob = async { + runtime.release() + } + + // ensure release is invoked before we assert on the job + delay(500) + + job.assertHandled(handle.getCompleted()) + + releaseJob.await() + } + + @TestTemplate fun `suspending during JS execution handled gracefully when releasing`() { + assertThrows { + runBlockingTest { + val (job, handle) = promiseOnJvmAsync() + handle.await() + + // ensure completable is awaited on before we release the runtime + delay(500) + + runtime.release() + + // this'll never complete b/c the promise will never resolve + job.assertHandled(handle.getCompleted()) + } + } + } +} diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableFieldTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableFieldTest.kt new file mode 100644 index 000000000..4589b945a --- /dev/null +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/bridge/serialization/serializers/NodeSerializableFieldTest.kt @@ -0,0 +1,173 @@ +package com.intuit.player.jvm.core.bridge.serialization.serializers + +import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.PlayerRuntimeException +import com.intuit.player.jvm.core.bridge.serialization.json.PrettyJson +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField.CacheStrategy +import com.intuit.player.jvm.core.player.PlayerException +import com.intuit.player.jvm.utils.test.RuntimeTest +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotSame +import org.junit.jupiter.api.Assertions.assertSame +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.TestTemplate +import org.junit.jupiter.api.assertThrows + +@Serializable +data class Structured( + val primitive: Int, + val nested: Structured? = null, +) + +@Serializable(Wrapper.Serializer::class) +class Wrapper(override val node: Node) : NodeWrapper { + val nested: Structured by NodeSerializableField() + val primitive: Int by NodeSerializableField() + + object Serializer : NodeWrapperSerializer(::Wrapper) +} + +internal class NodeSerializableFieldTest : RuntimeTest() { + + private val data = buildJsonObject { + put("primitive", 10) + put( + "nested", + buildJsonObject { + put("primitive", 20) + }, + ) + } + + lateinit var node: Node + + @BeforeEach + fun setup() { + runtime.execute("var data = ${PrettyJson.encodeToString(data)}") + node = runtime.getObject("data") ?: error("data not defined") + } + + @TestTemplate fun `default chooses no caching for primitive data`() { + val delegate = NodeSerializableField({ node }) + assertEquals(CacheStrategy.None, delegate.strategy) + + val primitive by delegate + assertEquals(10, primitive) + } + + @TestTemplate fun `default chooses smart caching for structured data`() { + val delegate = NodeSerializableField({ node }) + assertEquals(CacheStrategy.Smart, delegate.strategy) + + val nested by delegate + assertEquals(20, nested.primitive) + } + + @TestTemplate fun `smart caching only re-deserializes when JS instances differ`() { + val nested: Structured by NodeSerializableField({ node }, CacheStrategy.Smart) + + assertEquals(20, nested.primitive) + assertSame(nested, nested) + + val inst = nested + runtime.execute("data.nested = { primitive: 30 }") + + assertEquals(30, nested.primitive) + assertNotSame(inst, nested) + + val primitive: Int by NodeSerializableField({ node }, CacheStrategy.Smart) + assertEquals(10, primitive) + + // access check after runtime is released + runtime.release() + assertEquals("[$runtime] Runtime object has been released!", assertThrows { primitive }.message) + assertEquals("[$runtime] Runtime object has been released!", assertThrows { nested }.message) + } + + @TestTemplate fun `full caching doesn't re-access the JS layer`() { + val nested: Structured by NodeSerializableField({ node }, CacheStrategy.Full) + + assertEquals(20, nested.primitive) + assertSame(nested, nested) + + val inst = nested + runtime.execute("data.nested = { primitive: 30 }") + + assertEquals(20, nested.primitive) + assertSame(inst, nested) + + val primitive: Int by NodeSerializableField({ node }, CacheStrategy.Full) + assertEquals(10, primitive) + + // access check after runtime is released + runtime.release() + assertEquals(10, primitive) + assertEquals(Structured(20), nested) + } + + @TestTemplate fun `no caching always re-deserializes`() { + val nested: Structured by NodeSerializableField({ node }, CacheStrategy.None) + + assertEquals(20, nested.primitive) + assertNotSame(nested, nested) + + val inst = nested + runtime.execute("data.nested = { primitive: 30 }") + + assertEquals(30, nested.primitive) + assertNotSame(inst, nested) + + val primitive: Int by NodeSerializableField({ node }, CacheStrategy.None) + assertEquals(10, primitive) + + // access check after runtime is released + runtime.release() + assertEquals("[$runtime] Runtime object has been released!", assertThrows { primitive }.message) + assertEquals("[$runtime] Runtime object has been released!", assertThrows { nested }.message) + } + + @TestTemplate fun `throws when undefined`() { + val nested: Structured by NodeSerializableField({ node }, name = "undefined") + assertEquals( + "Could not deserialize \"undefined\" as \"com.intuit.player.jvm.core.bridge.serialization.serializers.Structured(primitive: kotlin.Int, nested: com.intuit.player.jvm.core.bridge.serialization.serializers.Structured?)\"", + assertThrows { + nested + }.message, + ) + } + + @TestTemplate fun `node wrapper helpers automatically provide node`() { + val wrapper: Wrapper by NodeSerializableField({ runtime }, name = "data") + + assertEquals(10, wrapper.primitive) + assertEquals(20, wrapper.nested.primitive) + } + + @TestTemplate fun `default value when non nullable returns null`() { + val delegate = NodeSerializableField({ node }, defaultValue = { 100 }) + val noKeyEntry by delegate + assertEquals(100, noKeyEntry) + } + + @TestTemplate fun `JsonNull is unfortunately not handled automatically`() { + var callCount = 0 + val json: JsonElement by NodeSerializableField({ runtime }) { callCount++; JsonNull } + assertEquals(JsonNull, json) + + runtime.execute("var json = 20;") + assertEquals(JsonPrimitive(20), json) + + runtime.execute("json = { hello: 'world' };") + assertEquals(JsonObject(mapOf("hello" to JsonPrimitive("world"))), json) + assertEquals(1, callCount) + } +} diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/data/DataControllerTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/data/DataControllerTest.kt index 6c8627990..2c302edfd 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/data/DataControllerTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/data/DataControllerTest.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.data import com.intuit.player.jvm.core.NodeBaseTest import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.getInvokable import io.mockk.every import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach @@ -10,7 +11,7 @@ import org.junit.jupiter.api.Test internal class DataControllerTest : NodeBaseTest() { var data: Map = mapOf( - "key" to "initial value" + "key" to "initial value", ) private val dataController by lazy { DataController(node) @@ -18,7 +19,7 @@ internal class DataControllerTest : NodeBaseTest() { @BeforeEach fun setUpMocks() { - every { node.getFunction("set") } returns Invokable { args -> + every { node.getInvokable("set") } returns Invokable { args -> val arg = args[0] as Map data = data + arg.keys.map { it to arg[it] } } @@ -29,14 +30,14 @@ internal class DataControllerTest : NodeBaseTest() { assertEquals("initial value", data["key"]) dataController.set( mapOf( - "key2" to 2 - ) + "key2" to 2, + ), ) assertEquals(2, data["key2"]) dataController.set( mapOf( - "key" to "1" - ) + "key" to "1", + ), ) assertEquals("1", data["key"]) assertEquals(2, data["key2"]) diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionSerializationTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionSerializationTest.kt index 23392c45f..8f9923a44 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionSerializationTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionSerializationTest.kt @@ -48,14 +48,14 @@ internal class ExpressionSerializationTest : RuntimeTest() { val expression: Expression.Single, val genericExpression: Expression, val expressions: Expression.Collection, - val genericExpressions: Expression + val genericExpressions: Expression, ) private val wrapper = ExprWrapper( single, genericSingle, collection, - genericCollection + genericCollection, ) @TestTemplate @@ -66,7 +66,7 @@ internal class ExpressionSerializationTest : RuntimeTest() { "genericExpression" to (genericSingle as Expression.Single).expression, "expressions" to collection.expressions, "genericExpressions" to (genericCollection as Expression.Collection).expressions, - ) + ), ) val wrapper = format.decodeFromRuntimeValue(ExprWrapper.serializer(), node) @@ -85,9 +85,9 @@ internal class ExpressionSerializationTest : RuntimeTest() { "genericExpression" to (genericSingle as Expression.Single).expression, "expressions" to collection.expressions, "genericExpressions" to (genericCollection as Expression.Collection).expressions, - ) + ), ), - runtime.serialize(ExprWrapper.serializer(), wrapper) + runtime.serialize(ExprWrapper.serializer(), wrapper), ) } } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionTest.kt index 87b9b752f..12aed3cef 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/expressions/ExpressionTest.kt @@ -1,6 +1,12 @@ package com.intuit.player.jvm.core.expressions -import kotlinx.serialization.json.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.add +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.jsonPrimitive import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -48,6 +54,7 @@ internal class ExpressionTest { assertEquals(collectionJson, Json.encodeToJsonElement(Expression.Collection.serializer(), collection)) assertEquals(genericCollectionJson, Json.encodeToJsonElement(Expression.serializer(), genericCollection)) } + @Test fun `test ExpressionType from JSON using implicit serializer`() { /** Uses [Expression.Single.Serializer] */ diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerIntegrationTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerIntegrationTest.kt index c20810b13..bab9770a7 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerIntegrationTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerIntegrationTest.kt @@ -106,7 +106,7 @@ internal class FlowControllerIntegrationTest : PlayerTest() { } } } -""" +""", ) val controller = player.inProgressState!!.controllers.flow diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerTest.kt index a12932060..6b90e5f95 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/flow/FlowControllerTest.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.core.flow import com.intuit.player.jvm.core.NodeBaseTest import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.getInvokable import io.mockk.every import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -16,7 +17,7 @@ internal class FlowControllerTest : NodeBaseTest() { @Test fun transition() { - every { node.getFunction("transition") } returns Invokable { + every { node.getInvokable("transition") } returns Invokable { lastTransition = it[0] as String } flowController.transition("Next") diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManagerTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManagerTest.kt index 0ac5e4077..4bb6e6cb4 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManagerTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/managed/AsyncIterationManagerTest.kt @@ -3,8 +3,12 @@ package com.intuit.player.jvm.core.managed import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.core.player.state.CompletedState import io.mockk.mockk -import kotlinx.coroutines.* +import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.yield import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import kotlin.coroutines.resume @@ -31,7 +35,7 @@ internal class AsyncIterationManagerTest { override suspend fun next(result: Boolean?): String? { throw PlayerException("expected") } - } + }, ) assertEquals(AsyncIterationManager.State.NotStarted, manager.state.value) manager.next() diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayerTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayerTest.kt index 1fa3ef0b6..cefe06ec2 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayerTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/HeadlessPlayerTest.kt @@ -1,6 +1,7 @@ package com.intuit.player.jvm.core.player import com.intuit.player.jvm.core.bridge.JSErrorException +import com.intuit.player.jvm.core.bridge.PlayerRuntimeException import com.intuit.player.jvm.core.bridge.global.JSMap import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.core.data.get @@ -10,23 +11,49 @@ import com.intuit.player.jvm.core.flow.Flow.Companion.UNKNOWN_ID import com.intuit.player.jvm.core.flow.forceTransition import com.intuit.player.jvm.core.flow.state.NavigationFlowStateType import com.intuit.player.jvm.core.flow.state.NavigationFlowViewState -import com.intuit.player.jvm.core.player.state.* +import com.intuit.player.jvm.core.flow.state.param +import com.intuit.player.jvm.core.player.state.CompletedState +import com.intuit.player.jvm.core.player.state.ErrorState +import com.intuit.player.jvm.core.player.state.InProgressState +import com.intuit.player.jvm.core.player.state.NotStartedState +import com.intuit.player.jvm.core.player.state.ReleasedState +import com.intuit.player.jvm.core.player.state.completedState +import com.intuit.player.jvm.core.player.state.currentFlowState +import com.intuit.player.jvm.core.player.state.currentView +import com.intuit.player.jvm.core.player.state.dataModel +import com.intuit.player.jvm.core.player.state.errorState +import com.intuit.player.jvm.core.player.state.inProgressState +import com.intuit.player.jvm.core.player.state.lastViewUpdate import com.intuit.player.jvm.core.plugins.Plugin import com.intuit.player.jvm.core.validation.BindingInstance import com.intuit.player.jvm.core.validation.ValidationResponse import com.intuit.player.jvm.core.validation.getWarningsAndErrors import com.intuit.player.jvm.utils.filterKeys -import com.intuit.player.jvm.utils.test.* +import com.intuit.player.jvm.utils.normalizeStackTraceElements +import com.intuit.player.jvm.utils.test.PlayerTest +import com.intuit.player.jvm.utils.test.ThreadUtils +import com.intuit.player.jvm.utils.test.mocks +import com.intuit.player.jvm.utils.test.runBlockingTest +import com.intuit.player.jvm.utils.test.simpleFlow +import com.intuit.player.jvm.utils.test.simpleFlowString import com.intuit.player.plugins.assets.ReferenceAssetsPlugin import com.intuit.player.plugins.types.CommonTypesPlugin +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.TestTemplate import kotlin.concurrent.thread +import kotlin.coroutines.resume internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { @@ -41,7 +68,7 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { val state = player.state assertTrue(state is NotStartedState) assertEquals(PlayerFlowStatus.NOT_STARTED, state.status) - assertNull(state.ref) + assertEquals("Symbol(not-started)", state.ref) } @TestTemplate @@ -101,17 +128,20 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { } } } -}""".trimMargin() +} + """.trimMargin(), ) player.inProgressState?.transition("Next") val result = flow.await() - assertEquals("done", result.endState.outcome) - assertEquals(mapOf("someKey" to "someValue"), result.endState["param"]) - assertEquals("extraValue", result.endState["extraKey"]) - assertEquals(mapOf("someInt" to 1), result.endState["extraObject"]) assertEquals("counter-flow", result.flow.id) + val endState = result.endState + assertEquals("done", endState.outcome) + player.release() + assertEquals(mapOf("someKey" to "someValue"), endState.param) + assertEquals("extraValue", endState["extraKey"]) + assertEquals(mapOf("someInt" to 1), endState["extraObject"]) } @TestTemplate @@ -166,7 +196,7 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { val state = player.state assertTrue(state is InProgressState) assertEquals(PlayerFlowStatus.IN_PROGRESS, state.status) - assertNull(state.ref) + assertEquals("Symbol(generated-flow)", state.ref) state as InProgressState val flowResultCompletable = state.flowResult @@ -175,8 +205,8 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { // remove evaluated nodes val currentViewJson = Json.decodeFromJsonElement( GenericSerializer(), - simpleFlow.views[0].jsonObject - .filterKeys("applicability") + simpleFlow.views!![0].jsonObject + .filterKeys("applicability"), ) // remove transforms @@ -233,7 +263,7 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { exception.printStackTrace() player.errorState?.error?.printStackTrace() - assertEquals(exception.stackTrace.toList(), player.errorState?.error?.stackTrace?.toList()) + assertEquals(exception.stackTrace.normalizeStackTraceElements(), player.errorState?.error?.stackTrace?.normalizeStackTraceElements()) } @TestTemplate @@ -270,7 +300,7 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { } assertEquals(message, exception.message) - assertEquals(message, player.errorState?.error?.message) + assertEquals("uncaught exception: $message", player.errorState?.error?.message) } @TestTemplate @@ -339,10 +369,19 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { @TestTemplate fun `test player released state`() = runBlockingTest { + val releasedStateTap: Deferred = async { + suspendCancellableCoroutine { + player.hooks.state.tap { state -> + if (state is ReleasedState) it.resume(state) + } + } + } + assertTrue(player.state is NotStartedState) player.release() assertTrue(player.state is ReleasedState) - val exception = assertThrows(PlayerException::class.java) { + assertEquals(ReleasedState, releasedStateTap.await()) + val exception = assertThrows(PlayerRuntimeException::class.java) { player.start(simpleFlowString) } assertEquals("Runtime object has been released!", exception.message?.split("] ")?.get(1)) @@ -400,7 +439,7 @@ internal class HeadlessPlayerTest : PlayerTest(), ThreadUtils { } } } - """.trimIndent() + """.trimIndent(), ) assertTrue(player.state is ErrorState) diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/PlayerHooksTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/PlayerHooksTest.kt index 45f8b5599..61548290f 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/PlayerHooksTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/PlayerHooksTest.kt @@ -1,8 +1,11 @@ package com.intuit.player.jvm.core.player import com.intuit.player.jvm.core.NodeBaseTest -import com.intuit.player.jvm.core.bridge.Invokable -import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 +import com.intuit.player.jvm.core.data.DataController +import com.intuit.player.jvm.core.flow.FlowController +import com.intuit.player.jvm.core.player.state.PlayerFlowState +import com.intuit.player.jvm.core.view.ViewController import io.mockk.every import io.mockk.impl.annotations.MockK import org.junit.jupiter.api.Assertions.assertNotNull @@ -11,13 +14,16 @@ import org.junit.jupiter.api.Test internal class PlayerHooksTest : NodeBaseTest() { @MockK - private lateinit var fc: Node + private lateinit var fc: NodeSyncHook1 + @MockK - private lateinit var vc: Node + private lateinit var vc: NodeSyncHook1 + @MockK - private lateinit var dc: Node + private lateinit var dc: NodeSyncHook1 + @MockK - private lateinit var state: Node + private lateinit var state: NodeSyncHook1 private val hooks by lazy { Player.Hooks(node) @@ -25,14 +31,12 @@ internal class PlayerHooksTest : NodeBaseTest() { @BeforeEach fun setUpMocks() { - every { node.getObject("flowController") } returns fc - every { node.getObject("viewController") } returns vc - every { node.getObject("dataController") } returns dc - every { node.getObject("state") } returns state - every { fc.getFunction("tap") } returns Invokable {} - every { vc.getFunction("tap") } returns Invokable {} - every { dc.getFunction("tap") } returns Invokable {} - every { state.getFunction("tap") } returns Invokable {} + every { node.getObject(any()) } returns node + every { node.getSerializable>("flowController", any()) } returns fc + every { node.getSerializable>("viewController", any()) } returns vc + every { node.getSerializable>("dataController", any()) } returns dc + every { node.getSerializable>("state", any()) } returns state + every { node.nativeReferenceEquals(any()) } returns true } @Test diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/CompletedStateTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/CompletedStateTest.kt index ca3daa6ec..791d9f464 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/CompletedStateTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/CompletedStateTest.kt @@ -1,18 +1,17 @@ package com.intuit.player.jvm.core.player.state import com.intuit.player.jvm.core.NodeBaseTest +import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node -import com.intuit.player.jvm.core.bridge.getSerializable +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat import com.intuit.player.jvm.core.data.DataModelWithParser -import com.intuit.player.jvm.core.expressions.ExpressionController import com.intuit.player.jvm.core.flow.Flow -import com.intuit.player.jvm.core.flow.FlowController import com.intuit.player.jvm.core.player.PlayerFlowStatus -import com.intuit.player.jvm.core.view.ViewController import io.mockk.every import io.mockk.impl.annotations.MockK -import kotlinx.serialization.modules.EmptySerializersModule +import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.BeforeEach @@ -32,21 +31,20 @@ internal class CompletedStateTest : NodeBaseTest() { @BeforeEach fun setUpMocks() { - every { node.getString("ref") } returns "someRef" - every { node.format } returns format - every { format.serializersModule } returns EmptySerializersModule - every { node.getSerializable("controllers", any()) } returns ControllerState(node) - every { node.getSerializable("view", any()) } returns ViewController(node) - every { node.getSerializable("flow", any()) } returns FlowController(node) - every { node.getSerializable("flow", any()) } returns FlowController(node) - every { node.getSerializable("expression", any()) } returns ExpressionController(node) + val runtime: Runtime<*> = mockk() + every { node.runtime } returns runtime + every { runtime.containsKey("getSymbol") } returns true + every { runtime.getInvokable("getSymbol") } returns Invokable { "Symbol(hello)" } every { node.getSerializable("flow", Flow.serializer()) } returns Flow("flowId") - every { node.getSerializable("dataModel", any()) } returns DataModelWithParser(node) + every { node.getSerializable("dataModel", DataModelWithParser.serializer()) } returns DataModelWithParser(node) + every { node.getObject("dataModel") } returns mockDataModel + every { node.getObject("flow") } returns null + every { node.nativeReferenceEquals(any()) } returns false } @Test fun ref() { - assertEquals("someRef", completedState.ref) + assertEquals("Symbol(hello)", completedState.ref) } @Test diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/ErrorStateTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/ErrorStateTest.kt index 900e8d87a..dfc95cc67 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/ErrorStateTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/ErrorStateTest.kt @@ -1,14 +1,15 @@ package com.intuit.player.jvm.core.player.state import com.intuit.player.jvm.core.NodeBaseTest -import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat +import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.flow.Flow import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.core.player.PlayerFlowStatus import io.mockk.every import io.mockk.mockk import kotlinx.serialization.KSerializer -import kotlinx.serialization.modules.EmptySerializersModule import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -21,20 +22,23 @@ internal class ErrorStateTest : NodeBaseTest() { @BeforeEach fun setUpMocks() { - every { node.getString("ref") } returns "someRef" + val runtime: Runtime<*> = mockk() + every { node.runtime } returns runtime + every { runtime.containsKey("getSymbol") } returns true + every { runtime.getInvokable("getSymbol") } returns Invokable { "Symbol(hello)" } every { node.getSerializable("flow", Flow.serializer()) } returns Flow("flowId") - - val mockFormat: RuntimeFormat<*> = mockk() - every { node.format } returns mockFormat - every { mockFormat.serializersModule } returns EmptySerializersModule + every { node.getSerializable("error", any>()) } returns null + every { node.getString("error") } returns "hello" + every { node.getObject("error") } returns null + every { node.getObject("flow") } returns null + every { node.nativeReferenceEquals(any()) } returns false } @Test fun errorFromObject() { val someException = PlayerException("hello") - every { node["error"] } returns node - every { node.deserialize(any>()) } returns someException + every { node.getSerializable("error", any>()) } returns someException assertEquals(someException, errorState.error) } @@ -48,7 +52,7 @@ internal class ErrorStateTest : NodeBaseTest() { @Test fun ref() { - assertEquals("someRef", errorState.ref) + assertEquals("Symbol(hello)", errorState.ref) } @Test diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/InProgressStateTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/InProgressStateTest.kt index d418e7c4b..f77844a04 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/InProgressStateTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/InProgressStateTest.kt @@ -3,10 +3,12 @@ package com.intuit.player.jvm.core.player.state import com.intuit.player.jvm.core.NodeBaseTest import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat -import com.intuit.player.jvm.core.bridge.serialization.format.serializer import com.intuit.player.jvm.core.bridge.toJson import com.intuit.player.jvm.core.data.DataController +import com.intuit.player.jvm.core.data.DataModelWithParser import com.intuit.player.jvm.core.expressions.ExpressionController import com.intuit.player.jvm.core.flow.Flow import com.intuit.player.jvm.core.flow.FlowController @@ -14,9 +16,12 @@ import com.intuit.player.jvm.core.player.PlayerFlowStatus import com.intuit.player.jvm.core.view.ViewController import io.mockk.every import io.mockk.impl.annotations.MockK +import io.mockk.mockk import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.modules.EmptySerializersModule -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -41,7 +46,12 @@ internal class InProgressStateTest : NodeBaseTest() { every { mockNode.getString("name") } returns "" every { mockNode.getObject("value") } returns mockNode every { mockNode.toJson() } returns JsonPrimitive("") - every { node.getString("ref") } returns "someRef" + every { node.getObject(any()) } returns node + every { node["flow"] } returns node + val runtime: Runtime<*> = mockk() + every { node.runtime } returns runtime + every { runtime.containsKey("getSymbol") } returns true + every { runtime.getInvokable("getSymbol") } returns Invokable { "Symbol(hello)" } every { node.format } returns format every { format.serializersModule } returns EmptySerializersModule every { node.getSerializable("controllers", any()) } returns controllerState @@ -51,15 +61,16 @@ internal class InProgressStateTest : NodeBaseTest() { every { node.getSerializable("current", any()) } returns null every { node.getSerializable("currentView", any()) } returns null every { node.getSerializable("data", any()) } returns DataController(node) - every { node.getFunction("getCurrentView") } returns null - every { node.getFunction("getLastViewUpdate") } returns Invokable { mockNode } - every { node.getFunction("getCurrentFlowState") } returns null + every { node.getInvokable("getCurrentView") } returns null + every { node.getInvokable("getLastViewUpdate") } returns Invokable { mockNode } + every { node.getInvokable("getCurrentFlowState") } returns null every { node.getObject("flowResult") } returns mockNode - every { node.getFunction("transition") } returns Invokable { + every { node.getInvokable("transition") } returns Invokable { lastTransition = it[0] as String } every { node.getSerializable("flow", Flow.serializer()) } returns Flow("flowId") - every { node.getObject("dataModel") } returns mockNode + every { node.getSerializable("dataModel", DataModelWithParser.serializer()) } returns DataModelWithParser(node) + every { node.nativeReferenceEquals(any()) } returns false } @Test @@ -95,7 +106,7 @@ internal class InProgressStateTest : NodeBaseTest() { @Test fun ref() { - assertEquals("someRef", inProgressState.ref) + assertEquals("Symbol(hello)", inProgressState.ref) } @Test diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/NotStartedStateTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/NotStartedStateTest.kt index c7d500349..3c407f8b5 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/NotStartedStateTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/player/state/NotStartedStateTest.kt @@ -1,8 +1,12 @@ package com.intuit.player.jvm.core.player.state import com.intuit.player.jvm.core.NodeBaseTest +import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.player.PlayerFlowStatus import io.mockk.every +import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -15,12 +19,15 @@ internal class NotStartedStateTest : NodeBaseTest() { @BeforeEach fun setUpMocks() { - every { node.getString("ref") } returns "someRef" + val runtime: Runtime<*> = mockk() + every { node.runtime } returns runtime + every { runtime.containsKey("getSymbol") } returns true + every { runtime.getInvokable("getSymbol") } returns Invokable { "Symbol(hello)" } } @Test fun ref() { - assertEquals("someRef", notStartedState.ref) + assertEquals("Symbol(hello)", notStartedState.ref) } @Test diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/plugins/PluggableTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/plugins/PluggableTest.kt index 4b5546035..84f393c74 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/plugins/PluggableTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/plugins/PluggableTest.kt @@ -1,6 +1,8 @@ package com.intuit.player.jvm.core.plugins -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test internal class PluggableTest { @@ -35,7 +37,7 @@ internal class PluggableTest { didWarn = true } override fun error(vararg args: Any?) = throw UnsupportedOperationException() - } + }, ) } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/DecodingTests.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/DecodingTests.kt index eefa599e6..a878add2a 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/DecodingTests.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/DecodingTests.kt @@ -10,7 +10,8 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.TestTemplate @Serializable @@ -111,7 +112,7 @@ internal class StructureDecoding : RuntimeTest() { "five" to 5, "six" to "six", ), - "seven" to 7.7 + "seven" to 7.7, ) assertEquals(map, format.decodeFromRuntimeValue(runtimeObject)) @@ -159,13 +160,13 @@ internal class StructureDecoding : RuntimeTest() { format.runtimeArray { append(1) append(2) - } + }, ) append( format.runtimeArray { append(3) append(4) - } + }, ) } @@ -185,13 +186,13 @@ internal class StructureDecoding : RuntimeTest() { format.runtimeArray { append(1) append("two") - } + }, ) append( format.runtimeArray { append(3) append(false) - } + }, ) append(7.7) } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/EncodingTests.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/EncodingTests.kt index b687d8746..a8cebb0f9 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/EncodingTests.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/EncodingTests.kt @@ -1,6 +1,8 @@ package com.intuit.player.jvm.core.serialization -import com.intuit.player.jvm.core.bridge.serialization.format.* +import com.intuit.player.jvm.core.bridge.serialization.format.encodeToRuntimeValue +import com.intuit.player.jvm.core.bridge.serialization.format.runtimeArray +import com.intuit.player.jvm.core.bridge.serialization.format.runtimeObject import com.intuit.player.jvm.utils.test.RuntimeTest import com.intuit.player.jvm.utils.test.equals import kotlinx.serialization.Serializable @@ -89,7 +91,7 @@ internal class StructureEncoding : RuntimeTest() { "five" to 5, "six" to "six", ), - "seven" to 7.7 + "seven" to 7.7, ) assertTrue(format.equals(runtimeObject, format.encodeToRuntimeValue(map))) @@ -136,13 +138,13 @@ internal class StructureEncoding : RuntimeTest() { format.runtimeArray { append(1) append(2) - } + }, ) append( format.runtimeArray { append(3) append(4) - } + }, ) } @@ -161,13 +163,13 @@ internal class StructureEncoding : RuntimeTest() { format.runtimeArray { append(1) append("two") - } + }, ) append( format.runtimeArray { append(3) append(false) - } + }, ) append(7.7) } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/NodeSerializationTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/NodeSerializationTest.kt index d479c324f..dfc741c16 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/NodeSerializationTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/serialization/NodeSerializationTest.kt @@ -2,11 +2,15 @@ package com.intuit.player.jvm.core.serialization import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.runtime.serialize +import com.intuit.player.jvm.core.bridge.serialization.serializers.Function1Serializer import com.intuit.player.jvm.utils.test.RuntimeTest import kotlinx.serialization.Polymorphic import kotlinx.serialization.Serializable -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.TestTemplate @Serializable @@ -16,13 +20,15 @@ data class SomeData( val genericInvokable: Invokable<@Polymorphic Any?>, val specificInvokable: (Int, String) -> Int, val specificInvokableWithNode: (Node) -> Map, + @Serializable(with = Function1Serializer::class) + val specificNonPrimitiveInvokable: (String) -> SomeDataWithDefaults?, val maybeGenericInvokable: Invokable<@Polymorphic Any?>? = null, val maybeNode: Node? = null, ) @Serializable data class SomeDataWithDefaults( - val name: String = "default" + val name: String = "default", ) internal class NodeSerializationTest : RuntimeTest() { @@ -32,6 +38,9 @@ internal class NodeSerializationTest : RuntimeTest() { private val genericInvokable: Invokable get() = Invokable { p1 -> println(p1); 2 } private val specificInvokable: (Int, String) -> Int get() = { p1, p2 -> println("p1: $p1; p2: $p2"); 3 } private val specificInvokableWithNode: (Node) -> Map get() = { p1 -> println(p1); p1 } + private val specificNonPrimitiveInvokable: (String) -> SomeDataWithDefaults? get() = { + SomeDataWithDefaults(it) + } @TestTemplate fun `serializes node wrappers`() { @@ -41,6 +50,7 @@ internal class NodeSerializationTest : RuntimeTest() { genericInvokable, specificInvokable, specificInvokableWithNode, + { null }, null, null, ) @@ -50,14 +60,14 @@ internal class NodeSerializationTest : RuntimeTest() { assertEquals(node, someDataObj["node"]) assertNotEquals(genericInvokable, someDataObj["genericInvokable"]) - assertEquals(2, someDataObj.getFunction("genericInvokable")?.invoke()) + assertEquals(2, someDataObj.getInvokable("genericInvokable")?.invoke()) assertNotEquals(specificInvokable, someDataObj["specificInvokable"]) - assertEquals(3, someDataObj.getFunction("specificInvokable")?.invoke(2, "three")) + assertEquals(3, someDataObj.getInvokable("specificInvokable")?.invoke(2, "three")) val param = mapOf("wut" to "where") assertNotEquals(specificInvokableWithNode, someDataObj["specificInvokableWithNode"]) - assertEquals(param, someDataObj.getFunction("specificInvokableWithNode")?.invoke(param)) + assertEquals(param, someDataObj.getInvokable("specificInvokableWithNode")?.invoke(param)) assertNull(someDataObj["maybeGenericInvokable"]) assertNull(someDataObj["maybeNode"]) @@ -71,8 +81,9 @@ internal class NodeSerializationTest : RuntimeTest() { "node" to node, "genericInvokable" to genericInvokable, "specificInvokableWithNode" to specificInvokableWithNode, + "specificNonPrimitiveInvokable" to specificNonPrimitiveInvokable, "specificInvokable" to specificInvokable, - ) + ), ) as Node val someData = someDataObj.deserialize(SomeData.serializer()) as SomeData assertEquals(name, someData.name) @@ -90,6 +101,11 @@ internal class NodeSerializationTest : RuntimeTest() { assertNull(someData.maybeGenericInvokable) assertNull(someData.maybeNode) + + val function = someData.specificNonPrimitiveInvokable + val data = function.invoke("Foo") + + assertEquals(SomeDataWithDefaults("Foo"), data) } @TestTemplate @@ -101,8 +117,9 @@ internal class NodeSerializationTest : RuntimeTest() { "genericInvokable" to genericInvokable, "specificInvokableWithNode" to specificInvokableWithNode, "specificInvokable" to specificInvokable, + "specificNonPrimitiveInvokable" to specificNonPrimitiveInvokable, "maybeNode" to Unit, - ) + ), ) as Node val someData = someDataObj.deserialize(SomeData.serializer()) as SomeData @@ -113,7 +130,7 @@ internal class NodeSerializationTest : RuntimeTest() { val someDataObj = runtime.serialize( mapOf( "name" to Unit, - ) + ), ) as Node val someData = someDataObj.deserialize(SomeDataWithDefaults.serializer()) diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooksTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooksTest.kt index 8a8e6db91..c8403b206 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooksTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerHooksTest.kt @@ -2,27 +2,31 @@ package com.intuit.player.jvm.core.view import com.intuit.player.jvm.core.NodeBaseTest import com.intuit.player.jvm.core.bridge.Invokable -import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 import io.mockk.every -import io.mockk.impl.annotations.MockK -import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import kotlin.test.assertEquals internal class ViewControllerHooksTest : NodeBaseTest() { - @MockK - private lateinit var view: Node + private val view by lazy { + NodeSyncHook1(node, View.serializer()) + } @BeforeEach fun setUpMock() { - every { node.getObject("view") } returns view - every { view.getFunction("tap") } returns Invokable {} + every { node.getObject("view") } returns node + every { node.getInvokable("tap") } returns Invokable {} + every { node.getInvokable("tap", any()) } returns Invokable {} + every { node.getSerializable>("view", any()) } returns view + every { node.nativeReferenceEquals(any()) } returns false } @Test fun view() { val viewControllerHooks = ViewController.Hooks(node) - assertNotNull(viewControllerHooks.view) + assertEquals(viewControllerHooks.view, view) } } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerTest.kt index a1c17ee70..c59d6b11f 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewControllerTest.kt @@ -12,6 +12,7 @@ internal class ViewControllerTest : NodeBaseTest() { @MockK private lateinit var hookNode: Node + @MockK private lateinit var viewNode: Node @@ -21,8 +22,10 @@ internal class ViewControllerTest : NodeBaseTest() { @BeforeEach fun setUpMock() { + every { node.getObject(any()) } returns node every { node.getSerializable("hooks", any()) } returns ViewController.Hooks(hookNode) every { node.getSerializable("currentView", any()) } returns View(viewNode) + every { node.nativeReferenceEquals(any()) } returns true } @Test diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewHooksTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewHooksTest.kt index 4cc6d1873..5d850ab00 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewHooksTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewHooksTest.kt @@ -1,21 +1,25 @@ package com.intuit.player.jvm.core.view import com.intuit.player.jvm.core.NodeBaseTest +import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.Invokable -import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.hooks.NodeSyncHook1 +import com.intuit.player.jvm.core.resolver.Resolver import io.mockk.every -import io.mockk.impl.annotations.MockK -import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test internal class ViewHooksTest : NodeBaseTest() { - @MockK - private lateinit var assetNode: Node + private val onUpdate by lazy { + NodeSyncHook1(node, Asset.serializer()) + } - @MockK - private lateinit var resolver: Node + private val resolver by lazy { + NodeSyncHook1(node, Resolver.serializer()) + } private val viewHooks by lazy { ViewHooks(node) @@ -23,19 +27,22 @@ internal class ViewHooksTest : NodeBaseTest() { @BeforeEach fun setUpMocks() { - every { node.getObject("onUpdate") } returns assetNode - every { node.getObject("resolver") } returns resolver - every { assetNode.getFunction("tap") } returns Invokable {} - every { resolver.getFunction("tap") } returns Invokable {} + every { node.getObject("onUpdate") } returns node + every { node.getObject("resolver") } returns node + every { node.getInvokable("tap") } returns Invokable {} + every { node.getInvokable("tap", any()) } returns Invokable {} + every { node.getSerializable>("onUpdate", any()) } returns onUpdate + every { node.getSerializable>("resolver", any()) } returns resolver + every { node.nativeReferenceEquals(any()) } returns false } @Test fun onUpdate() { - assertNotNull(viewHooks.onUpdate) + assertEquals(viewHooks.onUpdate, onUpdate) } @Test fun resolver() { - assertNotNull(viewHooks.resolver) + assertEquals(viewHooks.resolver, resolver) } } diff --git a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewTest.kt b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewTest.kt index 2e4a374c7..19aee0ca7 100644 --- a/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewTest.kt +++ b/jvm/core/src/test/kotlin/com/intuit/player/jvm/core/view/ViewTest.kt @@ -1,20 +1,21 @@ package com.intuit.player.jvm.core.view import com.intuit.player.jvm.core.NodeBaseTest -import com.intuit.player.jvm.core.bridge.Node import io.mockk.every -import io.mockk.impl.annotations.MockK -import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test internal class ViewTest : NodeBaseTest() { - @MockK - private lateinit var hooksNode: Node + private val hooks by lazy { + ViewHooks(node) + } @BeforeEach fun setUpMocks() { - every { node.getObject("hooks") } returns hooksNode + every { node.getObject("hooks") } returns node + every { node.getSerializable("hooks", any()) } returns hooks + every { node.nativeReferenceEquals(any()) } returns false } val view by lazy { @@ -23,6 +24,6 @@ internal class ViewTest : NodeBaseTest() { @Test fun hooks() { - assertNotNull(view.hooks) + assertEquals(view.hooks, hooks) } } diff --git a/jvm/dependencies/versions.bzl b/jvm/dependencies/versions.bzl index 72f5cf6c4..ae1e7aef0 100644 --- a/jvm/dependencies/versions.bzl +++ b/jvm/dependencies/versions.bzl @@ -1,17 +1,20 @@ versions = struct( kotlin = struct( - coroutines = "1.5.2", + coroutines = "1.6.0", serialization = "1.3.0", ), runtimes = struct( graaljs = "21.2.0", j2v8 = "6.1.0", ), + j2v8 = struct( + debugger = "0.2.3", + ), logging = struct( slf4j = "1.7.36", logback = "1.2.10", ), - hooks = "0.11.1", + hooks = "0.15.0", testing = struct( applitools = "4.7.6", junit = "4.12", @@ -43,4 +46,7 @@ versions = struct( ), material_dialogs = "3.3.0", material = "1.4.0", + facebook = struct( + stetho = "1.5.1", + ), ) diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNode.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNode.kt index cb1b39fdf..ff9ce4da7 100644 --- a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNode.kt +++ b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNode.kt @@ -7,8 +7,10 @@ import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat import com.intuit.player.jvm.graaljs.bridge.runtime.GraalRuntime.Companion.isReleased import com.intuit.player.jvm.graaljs.bridge.runtime.GraalRuntime.Companion.undefined -import com.intuit.player.jvm.graaljs.extensions.* +import com.intuit.player.jvm.graaljs.extensions.blockingLock import com.intuit.player.jvm.graaljs.extensions.handleValue +import com.intuit.player.jvm.graaljs.extensions.lockIfDefined +import com.intuit.player.jvm.graaljs.extensions.toInvokable import com.intuit.player.jvm.graaljs.extensions.toList import com.intuit.player.jvm.graaljs.extensions.toNode import kotlinx.serialization.DeserializationStrategy @@ -54,9 +56,13 @@ internal open class GraalNode(override val graalObject: Value, override val runt getMember(key) }?.handleValue(format) + override fun getInvokable(key: String, deserializationStrategy: DeserializationStrategy): Invokable? = graalObject.lockIfDefined { + graalObject.getMember(key) + }?.toInvokable(format, deserializationStrategy) + override fun getFunction(key: String): Invokable? = graalObject.lockIfDefined { graalObject.getMember(key) - }?.toInvokable(format) + }?.toInvokable(format, null) override fun getList(key: String): List<*>? = graalObject.lockIfDefined { graalObject.getMember(key) @@ -64,9 +70,9 @@ internal open class GraalNode(override val graalObject: Value, override val runt override fun getSerializable(key: String, deserializer: DeserializationStrategy): T? { return graalObject.blockingLock { - if (memberKeys.contains(key)) - format.decodeFromRuntimeValue(deserializer, graalObject.getMember(key)) - else null + key.takeIf(graalObject::hasMember) + ?.let(graalObject::getMember) + ?.let { format.decodeFromRuntimeValue(deserializer, it) } } } diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/runtime/GraalRuntime.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/runtime/GraalRuntime.kt index 2b6c85b59..5cd4b30e0 100644 --- a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/runtime/GraalRuntime.kt +++ b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/runtime/GraalRuntime.kt @@ -6,8 +6,10 @@ import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeConfig import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeContainer import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeFactory import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.serialization.serializers.playerSerializersModule import com.intuit.player.jvm.core.player.PlayerException +import com.intuit.player.jvm.core.utils.InternalPlayerApi import com.intuit.player.jvm.graaljs.bridge.GraalNode import com.intuit.player.jvm.graaljs.bridge.serialization.format.GraalFormat import com.intuit.player.jvm.graaljs.bridge.serialization.format.GraalFormatConfiguration @@ -26,13 +28,17 @@ import kotlinx.serialization.modules.plus import org.graalvm.polyglot.Context import org.graalvm.polyglot.Value import java.util.concurrent.locks.ReentrantLock +import kotlin.coroutines.EmptyCoroutineContext public fun Runtime(runtime: Context, config: GraalRuntimeConfig = GraalRuntimeConfig()): Runtime = GraalRuntime(config) internal class GraalRuntime( - private val config: GraalRuntimeConfig + override val config: GraalRuntimeConfig, ) : Runtime { + override val dispatcher: Nothing + get() = throw UnsupportedOperationException("dispatcher not defined for GraalRuntime") + val context: Context by config::graalContext override val format: GraalFormat = GraalFormat( @@ -40,13 +46,14 @@ internal class GraalRuntime( this, playerSerializersModule + SerializersModule { contextual(Value::class, GraalValueSerializer) - } - ) + }, + ), ) companion object { val Context.isReleased: Boolean - get() = contextRuntimeMap[this]?.released ?: throw PlayerException("Graal Context is not associated with a runtime") + get() = contextRuntimeMap[this]?.released + ?: throw PlayerException("Graal Context is not associated with a runtime") val Context.undefined: Value get() = eval("js", "undefined") private val contextRuntimeMap: MutableMap = hashMapOf() @@ -62,13 +69,17 @@ internal class GraalRuntime( } override val scope: CoroutineScope by lazy { - CoroutineScope(Dispatchers.Default + SupervisorJob()) + CoroutineScope(Dispatchers.Default + SupervisorJob() + (config.coroutineExceptionHandler ?: EmptyCoroutineContext)) } override fun execute(script: String): Any? = context.blockingLock { context.eval("js", script).handleValue(format) } + override fun load(scriptContext: ScriptContext) = context.blockingLock { + context.eval("js", scriptContext.script).handleValue(format) + } + override fun add(name: String, value: Value) { context.blockingLock { getBindings("js").putMember(name, value) @@ -87,6 +98,9 @@ internal class GraalRuntime( } } + @InternalPlayerApi + override var checkBlockingThread: Thread.() -> Unit = {} + override fun toString(): String = "Graal" private val backingNode: Node = GraalNode(context.getBindings("js"), this) @@ -101,6 +115,7 @@ internal class GraalRuntime( override fun isEmpty(): Boolean = backingNode.isEmpty() override fun getSerializable(key: String, deserializer: DeserializationStrategy): T? = backingNode.getSerializable(key, deserializer) + override fun deserialize(deserializer: DeserializationStrategy): T = backingNode.deserialize(deserializer) override fun isReleased(): Boolean = backingNode.isReleased() override fun isUndefined(): Boolean = backingNode.isUndefined() @@ -110,6 +125,7 @@ internal class GraalRuntime( override fun getDouble(key: String): Double? = backingNode.getDouble(key) override fun getLong(key: String): Long? = backingNode.getLong(key) override fun getBoolean(key: String): Boolean? = backingNode.getBoolean(key) + override fun getInvokable(key: String, deserializationStrategy: DeserializationStrategy): Invokable? = backingNode.getInvokable(key, deserializationStrategy) override fun getFunction(key: String): Invokable? = backingNode.getFunction(key) override fun getList(key: String): List<*>? = backingNode.getList(key) override fun getObject(key: String): Node? = backingNode.getObject(key) @@ -123,7 +139,7 @@ public object GraalJS : PlayerRuntimeFactory { } public data class GraalRuntimeConfig( - var graalContext: Context = PlayerContextFactory.context + var graalContext: Context = PlayerContextFactory.context, ) : PlayerRuntimeConfig() public class GraalRuntimeContainer : PlayerRuntimeContainer { diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalDecoders.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalDecoders.kt index a4e72339b..45fd32d7f 100644 --- a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalDecoders.kt +++ b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalDecoders.kt @@ -1,13 +1,20 @@ package com.intuit.player.jvm.graaljs.bridge.serialization.encoding -import com.intuit.player.jvm.core.bridge.serialization.encoding.* +import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeArrayListDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeObjectClassDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeObjectMapDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeValueDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.NodeDecoder import com.intuit.player.jvm.core.experimental.RuntimeClassDiscriminator import com.intuit.player.jvm.graaljs.bridge.runtime.GraalRuntime.Companion.undefined import com.intuit.player.jvm.graaljs.bridge.serialization.format.GraalDecodingException import com.intuit.player.jvm.graaljs.bridge.serialization.format.GraalFormat import com.intuit.player.jvm.graaljs.extensions.blockingLock import com.intuit.player.jvm.graaljs.extensions.handleValue +import com.intuit.player.jvm.graaljs.extensions.toInvokable import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind @@ -34,6 +41,10 @@ internal sealed class AbstractGraalDecoder( PolymorphicKind.SEALED -> GraalSealedClassDecoder(format, currentValue) else -> error("Runtime format decoders can't decode kinds of (${descriptor.kind}) into structures for $descriptor") } + + override fun decodeFunction(returnTypeSerializer: KSerializer): Invokable { + return currentValue.toInvokable(format, returnTypeSerializer) ?: error("Unable to decode Graal function using return type serializer ${returnTypeSerializer.descriptor}") + } } internal class GraalValueDecoder(format: GraalFormat, value: Value) : AbstractGraalDecoder(format, value) @@ -58,7 +69,7 @@ internal class GraalObjectMapDecoder(override val format: GraalFormat, override override fun buildDecoderForSerializableElement( descriptor: SerialDescriptor, index: Int, - deserializer: DeserializationStrategy + deserializer: DeserializationStrategy, ): Decoder = when (index % 2 == 0) { true -> GraalValueDecoder(format, format.context.asValue(getKeyAtIndex(index))) false -> GraalValueDecoder(format, getElementAtIndex(index)) @@ -80,7 +91,7 @@ internal class GraalArrayListDecoder(override val format: GraalFormat, override override fun buildDecoderForSerializableElement( descriptor: SerialDescriptor, index: Int, - deserializer: DeserializationStrategy + deserializer: DeserializationStrategy, ): Decoder = GraalValueDecoder(format, decodeElement(descriptor, index)) } @@ -104,7 +115,7 @@ internal class GraalObjectClassDecoder(override val format: GraalFormat, overrid override fun buildDecoderForSerializableElement( descriptor: SerialDescriptor, index: Int, - deserializer: DeserializationStrategy + deserializer: DeserializationStrategy, ): Decoder = GraalValueDecoder(format, decodeElement(descriptor, index)) } @@ -118,11 +129,10 @@ internal class GraalSealedClassDecoder(override val format: GraalFormat, overrid override fun buildDecoderForSerializableElement( descriptor: SerialDescriptor, index: Int, - deserializer: DeserializationStrategy + deserializer: DeserializationStrategy, ): Decoder = GraalValueDecoder(format, value) override fun decodeValueElement(descriptor: SerialDescriptor, index: Int): Any? { - val discriminator = ( descriptor.annotations.firstOrNull { it is RuntimeClassDiscriminator diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalEncoders.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalEncoders.kt index 2f0db8c98..3049c7bea 100644 --- a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalEncoders.kt +++ b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/encoding/GraalEncoders.kt @@ -56,14 +56,15 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v MAP, LIST, PRIMITIVE, - UNDECIDED + UNDECIDED, } private val currentContent get() = when (mode) { Mode.MAP -> contentMap Mode.LIST -> contentList Mode.PRIMITIVE, - Mode.UNDECIDED -> content + Mode.UNDECIDED, + -> content } private var tag: String? = null @@ -73,7 +74,8 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v } get() = when (mode) { Mode.UNDECIDED, - Mode.PRIMITIVE -> field + Mode.PRIMITIVE, + -> field else -> error("cannot get content unless in PRIMITIVE mode") } @@ -94,22 +96,26 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v } override fun beginStructure( - descriptor: SerialDescriptor + descriptor: SerialDescriptor, ): CompositeEncoder { val consumer = when (mode) { Mode.LIST, - Mode.MAP -> { node -> putContent(node) } + Mode.MAP, + -> { node -> putContent(node) } Mode.PRIMITIVE, - Mode.UNDECIDED -> consumer + Mode.UNDECIDED, + -> consumer } - return if (descriptor == ThrowableSerializer().descriptor) + return if (descriptor == ThrowableSerializer().descriptor) { GraalExceptionEncoder(format, ::putContent) - else when (descriptor.kind) { - StructureKind.CLASS -> GraalValueEncoder(format, Mode.MAP, consumer) - StructureKind.LIST, is PolymorphicKind -> GraalValueEncoder(format, Mode.LIST, consumer) - StructureKind.MAP -> GraalValueEncoder(format, Mode.MAP, consumer) - else -> GraalValueEncoder(format, consumer) + } else { + when (descriptor.kind) { + StructureKind.CLASS -> GraalValueEncoder(format, Mode.MAP, consumer) + StructureKind.LIST, is PolymorphicKind -> GraalValueEncoder(format, Mode.LIST, consumer) + StructureKind.MAP -> GraalValueEncoder(format, Mode.MAP, consumer) + else -> GraalValueEncoder(format, consumer) + } } } @@ -121,7 +127,7 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v is GraalNode -> value.graalObject is Value -> value else -> value - } + }, ) override fun encodeNull() = putContent(null) @@ -144,7 +150,7 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v .toTypedArray() format.encodeToGraalValue(invokable(*encodedArgs)) - } + }, ) override fun encodeFunction(kCallable: KCallable<*>) = putContent( @@ -160,15 +166,17 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v // check if type is nullable and value is null if (( - currValue == null && - // base type or type argument could be marked nullable - (kParam.type.isMarkedNullable || kParam.type.arguments[0].type?.isMarkedNullable == true) - ) || + currValue == null && + // base type or type argument could be marked nullable + (kParam.type.isMarkedNullable || kParam.type.arguments[0].type?.isMarkedNullable == true) + ) || // otherwise check if arg matches type if not null (currValue != null && currValue::class.isSubclassOf(kParam.type.arguments[0].type?.classifier as KClass<*>)) - ) + ) { index++ - else break + } else { + break + } } // only take matching args encodedArgs.slice(start until index).toTypedArray() @@ -178,7 +186,7 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v } }.toTypedArray() format.encodeToGraalValue(kCallable.call(*matchedArgs)) - } + }, ) override fun encodeFunction(function: Function<*>) { @@ -215,7 +223,7 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v is JsonArray -> value.toList() is JsonPrimitive -> value.value else -> value - } as T + } as T, ) else -> super.encodeSerializableValue(serializer, value) @@ -230,7 +238,8 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v else -> putContent(tag, content) } Mode.PRIMITIVE, - Mode.UNDECIDED -> { + Mode.UNDECIDED, + -> { this.content = format.context.asValue(content) endEncode() } @@ -242,7 +251,8 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v Mode.LIST -> contentList.add(content) Mode.MAP -> contentMap[tag] = content Mode.UNDECIDED, - Mode.PRIMITIVE -> { + Mode.PRIMITIVE, + -> { this.content = format.context.asValue(content) endEncode() } @@ -251,8 +261,10 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v } private fun Value.add(content: Any?) { - if (this.hasArrayElements()) blockingLock { - this.setArrayElement(this.arraySize, content) + if (this.hasArrayElements()) { + blockingLock { + this.setArrayElement(this.arraySize, content) + } } val item = "" } @@ -267,7 +279,8 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v is Boolean, is Double, is Int, - is Long -> putMember(key, content) + is Long, + -> putMember(key, content) else -> error("can't set property on Graal Value of type: ${content::class}") } } @@ -275,7 +288,8 @@ internal open class GraalValueEncoder(private val format: GraalFormat, private v internal class GraalExceptionEncoder(format: GraalFormat, consumer: (Value) -> Unit) : GraalValueEncoder( format, - Mode.MAP, consumer + Mode.MAP, + consumer, ) { override val contentMap by lazy { diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/format/GraalFormat.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/format/GraalFormat.kt index 28f4a501b..056d67ff2 100644 --- a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/format/GraalFormat.kt +++ b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/format/GraalFormat.kt @@ -14,7 +14,7 @@ import org.graalvm.polyglot.Context import org.graalvm.polyglot.Value public class GraalFormat( - config: GraalFormatConfiguration + config: GraalFormatConfiguration, ) : AbstractRuntimeFormat(config) { public val context: Context = (config.runtime as GraalRuntime).context @@ -31,7 +31,7 @@ public class GraalFormat( public data class GraalFormatConfiguration internal constructor( override val runtime: Runtime, - override val serializersModule: SerializersModule + override val serializersModule: SerializersModule, ) : RuntimeFormatConfiguration internal inline fun GraalFormat.encodeToGraalValue(value: T): Value = diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/handleValue.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/HandleValue.kt similarity index 52% rename from jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/handleValue.kt rename to jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/HandleValue.kt index 55e2d317c..ce5589192 100644 --- a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/handleValue.kt +++ b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/HandleValue.kt @@ -5,8 +5,11 @@ import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat import com.intuit.player.jvm.core.bridge.serialization.format.encodeToRuntimeValue +import com.intuit.player.jvm.core.bridge.serialization.format.serializer import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.graaljs.bridge.GraalNode +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationException import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import org.graalvm.polyglot.Value @@ -24,7 +27,8 @@ private fun Value.transform(format: RuntimeFormat): Any? = when { else -> hostObject } hasArrayElements() -> toList(format) - canExecute() -> toInvokable(format) + canExecute() -> toInvokable(format, format.serializer()) + metaObject.toString() == "symbol" -> null // this is also awful, but consistent w/ j2v8 else -> when (this.`as`(Any::class.java)) { is Int -> asInt() is Double, is Long -> try { asInt() } catch (e: Exception) { asDouble() } @@ -37,28 +41,52 @@ private fun Value.transform(format: RuntimeFormat): Any? = when { * Refer to the tests in NodeSerializationTest * */ is Map<*, *> -> if (canInvokeMember("getGraalObject")) invokeMember("getGraalObject").toNode(format) else toNode(format) - else -> null + else -> throw SerializationException("Value ($this) of type (${this::class.java}) is unknown") } } -internal fun Value.toList(format: RuntimeFormat): List? = if (isNull || !hasArrayElements()) null else lockIfDefined { - (0 until this.arraySize).map(::getArrayElement).map { it.handleValue(format) } +internal fun Value.toList(format: RuntimeFormat): List? = if (isNull || !hasArrayElements()) { + null +} else { + lockIfDefined { + (0 until this.arraySize).map(::getArrayElement).map { it.handleValue(format) } + } } -internal fun Value.toNode(format: RuntimeFormat): Node? = if (isNull || !hasMembers()) null else lockIfDefined { - if (this.hasMember("id") && this.hasMember("type")) - Asset(GraalNode(this, format.runtime)) - else GraalNode(this, format.runtime) +internal fun Value.toNode(format: RuntimeFormat): Node? = if (isNull || !hasMembers()) { + null +} else { + lockIfDefined { + if (this.hasMember("id") && this.hasMember("type")) { + Asset(GraalNode(this, format.runtime)) + } else { + GraalNode(this, format.runtime) + } + } } -internal fun Value.toInvokable(format: RuntimeFormat): Invokable? = if (isNull || !canExecute()) null else lockIfDefined { - Invokable { args -> - blockingLock { - this.execute( - *format.encodeToRuntimeValue( - args as Array - ).`as`(List::class.java).toTypedArray() - ).handleValue(format) as R +internal fun Value.toInvokable(format: RuntimeFormat, deserializationStrategy: DeserializationStrategy?): Invokable? = if (isNull || !canExecute()) { + null +} else { + lockIfDefined { + Invokable { args -> + blockingLock { + when ( + val result = + this.execute( + *format.encodeToRuntimeValue( + args as Array, + ).`as`(List::class.java).toTypedArray(), + ).handleValue(format) + ) { + is Node -> deserializationStrategy?.let { + result.deserialize(deserializationStrategy) + } ?: run { + result as R + } + else -> result as R + } + } } } } diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/lock.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/Lock.kt similarity index 99% rename from jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/lock.kt rename to jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/Lock.kt index 3d25e18bc..63b87d498 100644 --- a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/lock.kt +++ b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/Lock.kt @@ -71,7 +71,7 @@ internal fun Context.blockingLock(block: Context.() -> T): T = when { /** Special [blockingLock] helper to guard against undefined values */ internal fun Value.lockIfDefined( - block: Value.() -> T + block: Value.() -> T, ): T? = mapUndefinedToNull()?.let { blockingLock(block) } internal fun Value.mapUndefinedToNull() = blockingLock { diff --git a/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/unwrap.kt b/jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/Unwrap.kt similarity index 100% rename from jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/unwrap.kt rename to jvm/graaljs/src/main/kotlin/com/intuit/player/jvm/graaljs/extensions/Unwrap.kt diff --git a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/base/GraalTest.kt b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/base/GraalTest.kt index 98c345f77..82cf3acf1 100644 --- a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/base/GraalTest.kt +++ b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/base/GraalTest.kt @@ -30,8 +30,8 @@ internal abstract class GraalTest(val graal: Context = PlayerContextFactory.buil fun buildV8Object(jsonElement: JsonElement = buildJsonObject {}) = buildV8ObjectFromMap( Json.decodeFromJsonElement( MapSerializer(String.serializer(), GenericSerializer()), - jsonElement - ) + jsonElement, + ), ) fun buildV8ObjectFromMap(map: Map): Value = format.encodeToGraalValue(map) @@ -48,10 +48,9 @@ internal abstract class GraalTest(val graal: Context = PlayerContextFactory.buil fun Value.assertEquivalent(another: Any?) { Assertions.assertTrue( another is Value, - "value to compare is not a Graal Object: $another" + "value to compare is not a Graal Object: $another", ) (another as Value).let { - // verify that all missing keys from another are null or undefined (memberKeys.toSet() - another.memberKeys.toSet()).forEach { missingKey -> val actual = getMember(missingKey) @@ -66,12 +65,14 @@ internal abstract class GraalTest(val graal: Context = PlayerContextFactory.buil if (isNull || !hasMembers() || hasArrayElements()) { if (!canExecute()) Assertions.assertEquals(this.handleValue(format), another.handleValue(format)) - } else memberKeys.forEach { key -> - val (expected, actual) = getMember(key) to another.getMember(key) - if (expected is Value && !expected.isNull) { - expected.assertEquivalent(actual) - } else { - Assertions.assertEquals(expected, actual, "comparing key: $key") + } else { + memberKeys.forEach { key -> + val (expected, actual) = getMember(key) to another.getMember(key) + if (expected is Value && !expected.isNull) { + expected.assertEquivalent(actual) + } else { + Assertions.assertEquals(expected, actual, "comparing key: $key") + } } } } diff --git a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNodeTest.kt b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNodeTest.kt index f6d5d5035..e5de333b5 100644 --- a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNodeTest.kt +++ b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/GraalNodeTest.kt @@ -3,6 +3,7 @@ package com.intuit.player.jvm.graaljs.bridge import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.getJson import com.intuit.player.jvm.core.bridge.toJson import com.intuit.player.jvm.core.flow.Flow @@ -26,18 +27,18 @@ internal class GraalNodeTest : GraalTest() { "string" to "thisisastring", "int" to 1, "object" to mapOf( - "string" to "anotherstring" + "string" to "anotherstring", ), "list" to listOf( 1, "two", mapOf( - "string" to "onemorestring" + "string" to "onemorestring", ), - null + null, ), "function" to Invokable { "classicstring" }, - "null" to null + "null" to null, ) Assertions.assertEquals("thisisastring", node["string"]) @@ -55,7 +56,7 @@ internal class GraalNodeTest : GraalTest() { fun getString() { val node = buildNodeFromMap( "string" to "string", - "notastring" to 1 + "notastring" to 1, ) Assertions.assertEquals("string", node.getString("string")) @@ -68,20 +69,20 @@ internal class GraalNodeTest : GraalTest() { val node = buildNodeFromMap( "function" to Invokable { "classicstring" }, "tuple" to Invokable { (p0, p1) -> listOf(p0, p1) }, - "notafunction" to 1 + "notafunction" to 1, ) - Assertions.assertEquals("classicstring", node.getFunction("function")?.invoke()) - Assertions.assertEquals(listOf("1", 2), node.getFunction("tuple")?.invoke("1", 2)) - Assertions.assertEquals(null, node.getFunction("notafunction")) - Assertions.assertEquals(null, node.getFunction("notthere")) + Assertions.assertEquals("classicstring", node.getInvokable("function")?.invoke()) + Assertions.assertEquals(listOf("1", 2), node.getInvokable("tuple")?.invoke("1", 2)) + Assertions.assertEquals(null, node.getInvokable("notafunction")) + Assertions.assertEquals(null, node.getInvokable("notthere")) } @Test fun getList() { val node = buildNodeFromMap( "list" to listOf(1, 2, 3), - "notalist" to 1 + "notalist" to 1, ) Assertions.assertEquals(listOf(1, 2, 3), node.getList("list")) @@ -93,9 +94,9 @@ internal class GraalNodeTest : GraalTest() { fun getObject() { val node = buildNodeFromMap( "object" to mapOf( - "string" to "thisisastring" + "string" to "thisisastring", ), - "notaobject" to 1234 + "notaobject" to 1234, ) Assertions.assertEquals("thisisastring", node.getObject("object")?.getString("string")) @@ -109,8 +110,8 @@ internal class GraalNodeTest : GraalTest() { val node = buildNodeFromMap( "asset" to mapOf( "id" to "testId", - "type" to "testType" - ) + "type" to "testType", + ), ) val (id, type) = node.getObject("asset") as Asset @@ -124,14 +125,14 @@ internal class GraalNodeTest : GraalTest() { "assets" to listOf( mapOf( "id" to "testId1", - "type" to "testType" + "type" to "testType", ), mapOf( - "id" to "notAnAsset" + "id" to "notAnAsset", ), - 1 + 1, ), - "notassets" to "justastring" + "notassets" to "justastring", ) val assets = node.getList("assets") as List<*> @@ -153,7 +154,7 @@ internal class GraalNodeTest : GraalTest() { fun getInt() { val node = buildNodeFromMap( "int" to 1, - "notanint" to "asdf" + "notanint" to "asdf", ) Assertions.assertEquals(1, node.getInt("int")) @@ -164,7 +165,7 @@ internal class GraalNodeTest : GraalTest() { @Test fun getJson() { val node = buildNodeFromMap( - "beacon" to mapOf("key" to "value") + "beacon" to mapOf("key" to "value"), ) Assertions.assertEquals(JsonNull, node.getJson("notthere")) Assertions.assertEquals(buildJsonObject { put("key", "value") }, node.getJson("beacon")) @@ -185,17 +186,17 @@ internal class GraalNodeTest : GraalTest() { "beacon", buildJsonObject { put("key", "value") - } + }, ) }, - node.toJson() + node.toJson(), ) } @Test fun getBoolean() { val node = buildNodeFromMap( - "isSelected" to true + "isSelected" to true, ) Assertions.assertEquals(true, node.getBoolean("isSelected")) Assertions.assertNull(node.getBoolean("notthere")) @@ -207,18 +208,18 @@ internal class GraalNodeTest : GraalTest() { "string" to "thisisastring", "int" to 1, "object" to mapOf( - "string" to "anotherstring" + "string" to "anotherstring", ), "list" to listOf( 1, "two", mapOf( - "string" to "onemorestring" + "string" to "onemorestring", ), - null + null, ), "function" to Invokable { "classicstring" }, - "null" to null + "null" to null, ) addThreads( @@ -251,7 +252,7 @@ internal class GraalNodeTest : GraalTest() { Assertions.assertEquals(1, node.getInt("int")) } Assertions.assertEquals("thisisastring", node.get("string")) - } + }, ) startThreads() verifyThreads() @@ -260,7 +261,7 @@ internal class GraalNodeTest : GraalTest() { @Test fun getSerializablePrimitive() { val node = buildNodeFromMap( - "number" to 9 + "number" to 9, ) Assertions.assertEquals(9, node.getSerializable("number", Int.serializer())) } @@ -269,8 +270,8 @@ internal class GraalNodeTest : GraalTest() { fun getSerializable() { val node = buildNodeFromMap( "flow" to mapOf( - "id" to "testId" - ) + "id" to "testId", + ), ) Assertions.assertEquals("testId", node.getSerializable("flow", Flow.serializer())?.id) } diff --git a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/format/GraalFormatTest.kt b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/format/GraalFormatTest.kt index 460fa09c2..3896be5a3 100644 --- a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/format/GraalFormatTest.kt +++ b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/format/GraalFormatTest.kt @@ -2,6 +2,7 @@ package com.intuit.player.jvm.graaljs.bridge.format import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.graaljs.base.GraalTest import com.intuit.player.jvm.graaljs.bridge.serialization.format.encodeToGraalValue @@ -86,7 +87,7 @@ internal class GraalFormatTest : GraalTest() { val simple = format.decodeFromRuntimeValue( Simple.serializer(), - graalObject + graalObject, ) Assertions.assertEquals(3, simple.one) @@ -96,7 +97,7 @@ internal class GraalFormatTest : GraalTest() { @Test fun `decode into Node backed serializable`() = format.context.blockingLock { data class Simple(override val node: Node) : NodeWrapper { - fun increment(value: Int) = node.getFunction("increment")!!(value) + fun increment(value: Int) = node.getInvokable("increment")!!(value) } val graalObject = format.context.eval("js", "new Object()") @@ -104,12 +105,12 @@ internal class GraalFormatTest : GraalTest() { "increment", ProxyExecutable { args -> args[0].asInt() + 1 - } + }, ) val simple = format.decodeFromRuntimeValue( NodeWrapperSerializer(::Simple), - graalObject + graalObject, ) Assertions.assertEquals(1, simple.increment(0)) @@ -129,12 +130,12 @@ internal class GraalFormatTest : GraalTest() { "increment", ProxyExecutable { args -> args[0].asInt() + 1 - } + }, ) val simple = format.decodeFromRuntimeValue( Data.serializer(), - graalObject + graalObject, ) Assertions.assertEquals(3, simple.one) diff --git a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/DecodingTest.kt b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/DecodingTest.kt index 1c01b9ff5..3c7a6ee3f 100644 --- a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/DecodingTest.kt +++ b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/DecodingTest.kt @@ -55,7 +55,7 @@ internal class FunctionDecoding : GraalTest() { Assertions.assertEquals("PLAYER: 1", function.execute(eval("js", "'PLAYER'"), eval("js", "1"))) Assertions.assertEquals( "PLAYER: 2", - format.decodeFromGraalValue>(graalObject.getMember("method"))("PLAYER", 2) + format.decodeFromGraalValue>(graalObject.getMember("method"))("PLAYER", 2), ) } @@ -70,14 +70,14 @@ internal class FunctionDecoding : GraalTest() { Assertions.assertEquals("PLAYER: 1", function.execute(eval("js", "'PLAYER'"), eval("js", "1"))) Assertions.assertEquals( "PLAYER: 2", - format.decodeFromGraalValue>(graalObject.getMember("method"))("PLAYER", 2) + format.decodeFromGraalValue>(graalObject.getMember("method"))("PLAYER", 2), ) } @Test fun `decode kcallable`() = format.context.blockingLock { @Serializable data class Container( - val method: (String, Int) -> String + val method: (String, Int) -> String, ) val function = ProxyExecutable { @@ -90,8 +90,8 @@ internal class FunctionDecoding : GraalTest() { format.decodeFromGraalValue( eval("js", "new Object()").also { it.putMember("method", function) - } - ).method("PLAYER", 2) + }, + ).method("PLAYER", 2), ) } } diff --git a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/EncodingTest.kt b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/EncodingTest.kt index 327198441..cdfa25338 100644 --- a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/EncodingTest.kt +++ b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/EncodingTest.kt @@ -53,7 +53,7 @@ internal class FunctionEncoding : GraalTest() { Assertions.assertEquals("PLAYER: 1", callback("PLAYER", 1)) Assertions.assertEquals( "PLAYER: 2", - format.encodeToGraalValue(callback).execute("PLAYER", 2).asString() + format.encodeToGraalValue(callback).execute("PLAYER", 2).asString(), ) } @@ -63,7 +63,7 @@ internal class FunctionEncoding : GraalTest() { Assertions.assertEquals("PLAYER: 1", callback("PLAYER", 1)) Assertions.assertEquals( "PLAYER: 2", - format.encodeToGraalValue(callback).execute("PLAYER", 2).asString() + format.encodeToGraalValue(callback).execute("PLAYER", 2).asString(), ) } @@ -77,7 +77,7 @@ internal class FunctionEncoding : GraalTest() { Assertions.assertEquals("PLAYER: 1", callback("PLAYER", 1)) Assertions.assertEquals( "PLAYER: 2", - format.encodeToGraalValue(callback).execute("PLAYER", 2).asString() + format.encodeToGraalValue(callback).execute("PLAYER", 2).asString(), ) } } diff --git a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/JsonEncodingTests.kt b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/JsonEncodingTests.kt index 2102ad32d..8b7c3d5e4 100644 --- a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/JsonEncodingTests.kt +++ b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/bridge/serialization/JsonEncodingTests.kt @@ -21,7 +21,7 @@ internal class JsonEncodingTests : GraalTest() { buildJsonObject { put("state_type", "END") put("outcome", "doneWithTopic") - } + }, ) } private val expectedJsonString = expectedJson.toString() diff --git a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/promise/GraalPromiseTest.kt b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/promise/GraalPromiseTest.kt index 865b8c1f0..b1cb94bab 100644 --- a/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/promise/GraalPromiseTest.kt +++ b/jvm/graaljs/src/test/kotlin/com/intuit/player/jvm/graaljs/promise/GraalPromiseTest.kt @@ -21,7 +21,7 @@ internal class GraalPromiseTest : GraalTest(), PromiseUtils { const promise = new Promise(function(resolve, reject) { asdf.asdf.asdf.asdf }); return [promise, resolver]; })(); - """.trimIndent() + """.trimIndent(), ) as List<*> Promise(promise as Node).thenRecord.catchRecord @@ -39,7 +39,7 @@ internal class GraalPromiseTest : GraalTest(), PromiseUtils { at .(Unnamed:3) at .(Unnamed:1) """, - exception.stackTraceToString() + exception.stackTraceToString(), ) } } diff --git a/jvm/j2v8/api/j2v8.api b/jvm/j2v8/api/j2v8.api index faad36e9b..e8cef6c68 100644 --- a/jvm/j2v8/api/j2v8.api +++ b/jvm/j2v8/api/j2v8.api @@ -29,8 +29,15 @@ public final class com/intuit/player/jvm/j2v8/bridge/runtime/J2V8RuntimeContaine } public final class com/intuit/player/jvm/j2v8/bridge/runtime/V8RuntimeKt { + public static final fun Runtime ()Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; + public static final fun Runtime (Lcom/eclipsesource/v8/V8;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; public static final fun Runtime (Lcom/eclipsesource/v8/V8;Lcom/intuit/player/jvm/j2v8/bridge/runtime/J2V8RuntimeConfig;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; + public static final fun Runtime (Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; + public static final fun Runtime (Ljava/lang/String;Ljava/lang/String;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; + public static final fun Runtime (Ljava/lang/String;Ljava/nio/file/Path;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; public static synthetic fun Runtime$default (Lcom/eclipsesource/v8/V8;Lcom/intuit/player/jvm/j2v8/bridge/runtime/J2V8RuntimeConfig;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; + public static synthetic fun Runtime$default (Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; + public static synthetic fun Runtime$default (Ljava/lang/String;Ljava/nio/file/Path;ILjava/lang/Object;)Lcom/intuit/player/jvm/core/bridge/runtime/Runtime; } public final class com/intuit/player/jvm/j2v8/bridge/serialization/format/BuildersKt { diff --git a/jvm/j2v8/deps.bzl b/jvm/j2v8/deps.bzl index 1eed9a99f..1bff1a39d 100644 --- a/jvm/j2v8/deps.bzl +++ b/jvm/j2v8/deps.bzl @@ -1,18 +1,23 @@ load("//jvm/dependencies:versions.bzl", "versions") +load("@rules_player//maven:parse_coordinates.bzl", "parse_coordinates") -maven = [] +maven = [ + "com.github.AlexTrotsenko:j2v8-debugger:%s" % versions.j2v8.debugger, +] main_exports = [ "//jvm/core", ] -main_deps = main_exports + [ +main_deps = main_exports + parse_coordinates(maven) + [ "//jvm:kotlin_serialization", - "//jvm/j2v8/libs:j2v8_empty" + + # TODO: Ensure all of these are _just_ compileOnly deps + "//jvm/j2v8/libs:j2v8_empty", ] main_resources = [ - "//core/player:Player_Bundles" + "//core/player:Player_Bundles", ] test_deps = [ diff --git a/jvm/j2v8/libs/BUILD b/jvm/j2v8/libs/BUILD index ca87885b0..5664dd127 100644 --- a/jvm/j2v8/libs/BUILD +++ b/jvm/j2v8/libs/BUILD @@ -3,18 +3,18 @@ package(default_visibility = ["//jvm/j2v8:__pkg__"]) java_import( name = "j2v8_empty", jars = [":j2v8_empty-6.1.0.jar"], - neverlink = True + neverlink = True, ) java_import( name = "j2v8_macos", jars = [ ":j2v8_macos_x86_64-6.1.0.jar", - ":j2v8_macos_aarch_64-6.1.0.jar" + ":j2v8_macos_aarch_64-6.1.0.jar", ], ) java_import( name = "j2v8_linux", jars = [":j2v8_linux_x86_64-6.1.0.jar"], -) \ No newline at end of file +) diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/V8Primitive.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/V8Primitive.kt index 7aaae1eb4..ebe12197c 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/V8Primitive.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/V8Primitive.kt @@ -1,9 +1,14 @@ package com.intuit.player.jvm.j2v8 -import com.eclipsesource.v8.* +import com.eclipsesource.v8.V8 +import com.eclipsesource.v8.V8Array +import com.eclipsesource.v8.V8Function +import com.eclipsesource.v8.V8Object +import com.eclipsesource.v8.V8Value +import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8Format import com.intuit.player.jvm.j2v8.bridge.serialization.format.encodeToV8Value -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking /** * Primitive wrapper for [V8Value]s. The intent behind this construct is to enable the @@ -53,8 +58,12 @@ internal val Context.v8Function: V8Function get() = this as? ?: throw IllegalArgumentException("Element ${this::class} is not a V8Function") // [get] helpers for wrapping primitive values -internal fun V8Object.getV8Value(key: String): V8Value = blockingLock { get(key) }.let(::V8Value) -internal fun V8Array.getV8Value(index: Int): V8Value = blockingLock { get(index) }.let(::V8Value) +internal fun V8Object.getV8Value(runtime: Runtime, key: String): V8Value = evaluateInJSThreadBlocking(runtime) { + get(key).let(::V8Value) +} +internal fun V8Array.getV8Value(runtime: Runtime, index: Int): V8Value = evaluateInJSThreadBlocking(runtime) { + get(index).let(::V8Value) +} internal fun V8Value(content: Any?): V8Value = when (content) { is V8Value -> content @@ -87,9 +96,9 @@ internal fun Context.V8Array(block: V8Array.() -> Unit = {}) * This _should_ be the main entry point for creating [V8Function]s within this module b/c it takes into account * runtime locking and ensuring that the return value can be appropriately handled by J2V8 */ -internal inline fun V8Function(format: J2V8Format, crossinline block: V8Object.(args: V8Array) -> T): V8Function = format.v8.blockingLock { +internal inline fun V8Function(format: J2V8Format, crossinline block: V8Object.(args: V8Array) -> T): V8Function = format.v8.evaluateInJSThreadBlocking(format.runtime) { V8Function(this) { receiver, args -> - receiver.blockingLock { + receiver.evaluateInJSThreadBlocking(format.runtime) { when (val retVal = format.encodeToV8Value(block(args))) { is V8Primitive -> retVal.value else -> retVal diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/V8Node.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/V8Node.kt index ffc2d747f..7091c93c9 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/V8Node.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/V8Node.kt @@ -1,10 +1,22 @@ package com.intuit.player.jvm.j2v8.bridge -import com.eclipsesource.v8.* -import com.intuit.player.jvm.core.bridge.* +import com.eclipsesource.v8.V8 +import com.eclipsesource.v8.V8Array +import com.eclipsesource.v8.V8Function +import com.eclipsesource.v8.V8Object +import com.eclipsesource.v8.V8Value +import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.NodeWrapper import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat -import com.intuit.player.jvm.j2v8.extensions.* +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadIfDefinedBlocking +import com.intuit.player.jvm.j2v8.extensions.handleValue +import com.intuit.player.jvm.j2v8.extensions.mapUndefinedToNull +import com.intuit.player.jvm.j2v8.extensions.toInvokable +import com.intuit.player.jvm.j2v8.extensions.toList +import com.intuit.player.jvm.j2v8.extensions.toNode import com.intuit.player.jvm.j2v8.getV8Value import kotlinx.serialization.DeserializationStrategy @@ -16,7 +28,7 @@ internal class V8Node(override val v8Object: V8Object, override val runtime: Run override val format: RuntimeFormat get() = runtime.format override val keys: Set by lazy { - v8Object.lockIfDefined { + v8Object.evaluateInJSThreadIfDefinedBlocking(runtime) { keys.filter { v8Object.get(it) != V8.getUndefined() }.toSet() } ?: emptySet() } @@ -40,26 +52,30 @@ internal class V8Node(override val v8Object: V8Object, override val runtime: Run override fun isEmpty(): Boolean = size == 0 // Getter APIs - override operator fun get(key: String): Any? = v8Object.lockIfDefined { + override operator fun get(key: String): Any? = v8Object.evaluateInJSThreadIfDefinedBlocking(runtime) { get(key).handleValue(format) } - override fun getFunction(key: String): Invokable? = v8Object.lockIfDefined { + override fun getInvokable(key: String, deserializationStrategy: DeserializationStrategy): Invokable? = v8Object.evaluateInJSThreadIfDefinedBlocking(runtime) { get(key) as? V8Function - }?.toInvokable(format, v8Object) + }?.toInvokable(format, v8Object, deserializationStrategy) - override fun getList(key: String): List<*>? = v8Object.lockIfDefined { + override fun getFunction(key: String): Invokable? = v8Object.evaluateInJSThreadIfDefinedBlocking(runtime) { + get(key) as? V8Function + }?.toInvokable(format, v8Object, null) + + override fun getList(key: String): List<*>? = v8Object.evaluateInJSThreadIfDefinedBlocking(runtime) { get(key) as? V8Array }?.toList(format) - override fun getObject(key: String): Node? = v8Object.lockIfDefined { + override fun getObject(key: String): Node? = v8Object.evaluateInJSThreadIfDefinedBlocking(runtime) { get(key) as? V8Object }?.toNode(format) - override fun getSerializable(key: String, deserializer: DeserializationStrategy): T? = v8Object.blockingLock { - if (keys.contains(key)) - format.decodeFromRuntimeValue(deserializer, getV8Value(key)) - else null + override fun getSerializable(key: String, deserializer: DeserializationStrategy): T? = v8Object.evaluateInJSThreadBlocking(runtime) { + getV8Value(this@V8Node.runtime, key).mapUndefinedToNull()?.let { + format.decodeFromRuntimeValue(deserializer, it) + } } override fun deserialize(deserializer: DeserializationStrategy): T = format.decodeFromRuntimeValue(deserializer, v8Object) @@ -71,7 +87,7 @@ internal class V8Node(override val v8Object: V8Object, override val runtime: Run override fun nativeReferenceEquals(other: Any?): Boolean = when (other) { is NodeWrapper -> nativeReferenceEquals(other.node) is V8ObjectWrapper -> nativeReferenceEquals(other.v8Object) - is V8Object -> v8Object.blockingLock { + is V8Object -> v8Object.evaluateInJSThreadBlocking(runtime) { v8Object.strictEquals(other) } else -> false @@ -87,9 +103,9 @@ internal class V8Node(override val v8Object: V8Object, override val runtime: Run else -> false } - override fun hashCode(): Int = v8Object.blockingLock { hashCode() } + override fun hashCode(): Int = v8Object.evaluateInJSThreadBlocking(runtime) { hashCode() } - override fun toString(): String = v8Object.lockIfDefined { + override fun toString(): String = v8Object.evaluateInJSThreadIfDefinedBlocking(runtime) { keys.associate { it to get(it) }.toString() } ?: emptyMap().toString() } diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/runtime/V8Runtime.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/runtime/V8Runtime.kt index 89697fc4e..a2f2a645e 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/runtime/V8Runtime.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/runtime/V8Runtime.kt @@ -1,5 +1,7 @@ package com.intuit.player.jvm.j2v8.bridge.runtime +import com.alexii.j2v8debugger.ScriptSourceProvider +import com.alexii.j2v8debugger.V8Debugger import com.eclipsesource.v8.V8 import com.eclipsesource.v8.V8Array import com.eclipsesource.v8.V8Object @@ -7,8 +9,15 @@ import com.eclipsesource.v8.V8Value import com.eclipsesource.v8.utils.MemoryManager import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node -import com.intuit.player.jvm.core.bridge.runtime.* +import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeConfig +import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeContainer +import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeFactory +import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.serialization.serializers.playerSerializersModule +import com.intuit.player.jvm.core.player.PlayerException +import com.intuit.player.jvm.core.utils.InternalPlayerApi +import com.intuit.player.jvm.core.utils.await import com.intuit.player.jvm.j2v8.V8Null import com.intuit.player.jvm.j2v8.V8Primitive import com.intuit.player.jvm.j2v8.addPrimitive @@ -16,21 +25,46 @@ import com.intuit.player.jvm.j2v8.bridge.V8Node import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8Format import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8FormatConfiguration import com.intuit.player.jvm.j2v8.bridge.serialization.serializers.V8ValueSerializer -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import com.intuit.player.jvm.j2v8.extensions.handleValue import com.intuit.player.jvm.j2v8.extensions.unlock -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExecutorCoroutineDispatcher +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.cancel +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.plus - +import java.nio.file.Path +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import kotlin.coroutines.EmptyCoroutineContext +import kotlin.io.path.createTempDirectory +import kotlin.io.path.pathString + +@JvmOverloads public fun Runtime(runtime: V8, config: J2V8RuntimeConfig = J2V8RuntimeConfig(runtime)): Runtime = V8Runtime(config) -internal class V8Runtime(private val config: J2V8RuntimeConfig) : Runtime { +public fun Runtime(globalAlias: String? = null, tempDir: Path? = null): Runtime = + Runtime(V8.createV8Runtime(globalAlias, tempDir?.pathString).unlock()) + +// TODO: Do a better job of exposing runtime args as Config params to limit the need for these +@JvmOverloads +public fun Runtime(globalAlias: String? = null, tempDirPrefix: String? = null): Runtime = + Runtime(globalAlias, tempDirPrefix?.let(::createTempDirectory)) + +internal class V8Runtime(override val config: J2V8RuntimeConfig) : Runtime, ScriptSourceProvider { + + lateinit var v8: V8; private set - val v8: V8 by config::runtime + override val dispatcher: ExecutorCoroutineDispatcher = config.executorService.asCoroutineDispatcher() override val format: J2V8Format = J2V8Format( J2V8FormatConfiguration( @@ -41,22 +75,53 @@ internal class V8Runtime(private val config: J2V8RuntimeConfig) : Runtime addPrimitive(name, value) else -> add(name, value) @@ -64,19 +129,34 @@ internal class V8Runtime(private val config: J2V8RuntimeConfig) : Runtime serialize(serializer: SerializationStrategy, value: T): Any? = v8.blockingLock { + override fun serialize(serializer: SerializationStrategy, value: T): Any? = v8.evaluateInJSThreadBlocking(runtime) { format.encodeToRuntimeValue(serializer, value).handleValue(format) } override fun release() { - v8.blockingLock { - scope.cancel() + // cancel work in runtime scope + scope.cancel("releasing runtime") + // swap to dispatcher to release everything + runBlocking(dispatcher) { memoryScope.release() - runtime.release(true) + v8.release(true) } + // close dispatcher + dispatcher.close() } - override fun toString() = "J2V8" + @InternalPlayerApi + override var checkBlockingThread: Thread.() -> Unit = {} + + private val scriptMapping = mutableMapOf() + + private val scriptIds: MutableSet = mutableSetOf() + override val allScriptIds: Collection + get() = scriptIds.toSet() + + override fun getSource(scriptId: String): String = scriptMapping[scriptId] ?: throw PlayerException("Script with name $scriptId not available for debugging, was it loaded?") + + override fun toString(): String = "J2V8" // Delegated Node members private val backingNode: Node = V8Node(v8, this) @@ -92,6 +172,7 @@ internal class V8Runtime(private val config: J2V8RuntimeConfig) : Runtime getSerializable(key: String, deserializer: DeserializationStrategy): T? = backingNode.getSerializable(key, deserializer) + override fun deserialize(deserializer: DeserializationStrategy): T = backingNode.deserialize(deserializer) override fun isReleased(): Boolean = backingNode.isReleased() override fun isUndefined(): Boolean = backingNode.isUndefined() @@ -101,6 +182,7 @@ internal class V8Runtime(private val config: J2V8RuntimeConfig) : Runtime getInvokable(key: String, deserializationStrategy: DeserializationStrategy): Invokable? = backingNode.getInvokable(key, deserializationStrategy) override fun getFunction(key: String): Invokable? = backingNode.getFunction(key) override fun getList(key: String): List<*>? = backingNode.getList(key) override fun getObject(key: String): Node? = backingNode.getObject(key) @@ -114,8 +196,19 @@ public object J2V8 : PlayerRuntimeFactory { } public data class J2V8RuntimeConfig( - var runtime: V8 = V8.createV8Runtime().unlock(), -) : PlayerRuntimeConfig() + var runtime: V8? = null, + private val explicitExecutorService: ExecutorService? = null, + override var debuggable: Boolean = false, + override var coroutineExceptionHandler: CoroutineExceptionHandler? = null, +) : PlayerRuntimeConfig() { + public val executorService: ExecutorService by lazy { + explicitExecutorService ?: Executors.newSingleThreadExecutor { + Executors.defaultThreadFactory().newThread(it).apply { + name = "js-runtime" + } + } + } +} public class J2V8RuntimeContainer : PlayerRuntimeContainer { override val factory: PlayerRuntimeFactory<*> = J2V8 diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Decoders.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Decoders.kt index 5cc55e06c..cdee89eaa 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Decoders.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Decoders.kt @@ -3,14 +3,26 @@ package com.intuit.player.jvm.j2v8.bridge.serialization.encoding import com.eclipsesource.v8.V8Array import com.eclipsesource.v8.V8Object import com.eclipsesource.v8.V8Value -import com.intuit.player.jvm.core.bridge.serialization.encoding.* +import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeArrayListDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeObjectClassDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeObjectMapDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.AbstractRuntimeValueDecoder +import com.intuit.player.jvm.core.bridge.serialization.encoding.NodeDecoder import com.intuit.player.jvm.core.experimental.RuntimeClassDiscriminator -import com.intuit.player.jvm.j2v8.* +import com.intuit.player.jvm.j2v8.V8Null +import com.intuit.player.jvm.j2v8.V8Primitive import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8DecodingException import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8Format -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import com.intuit.player.jvm.j2v8.extensions.handleValue -import kotlinx.serialization.* +import com.intuit.player.jvm.j2v8.extensions.toInvokable +import com.intuit.player.jvm.j2v8.getV8Value +import com.intuit.player.jvm.j2v8.v8Array +import com.intuit.player.jvm.j2v8.v8Function +import com.intuit.player.jvm.j2v8.v8Object +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind @@ -35,6 +47,10 @@ internal sealed class AbstractV8Decoder( PolymorphicKind.SEALED -> V8SealedClassDecoder(format, currentValue.v8Object) else -> error("Runtime format decoders can't decode kinds of (${descriptor.kind}) into structures for $descriptor") } + + override fun decodeFunction(returnTypeSerializer: KSerializer): Invokable { + return currentValue.v8Function.toInvokable(format, currentValue.v8Function, returnTypeSerializer) ?: error("Unable to decode V8 function using return type serializer ${returnTypeSerializer.descriptor}") + } } /** Simple implementation of [AbstractV8Decoder] can be treated as the entry point for [value] decoding */ @@ -42,14 +58,14 @@ internal class V8ValueDecoder(format: J2V8Format, value: V8Value) : AbstractV8De internal class V8ObjectMapDecoder(override val format: J2V8Format, override val value: V8Object) : AbstractRuntimeObjectMapDecoder(), NodeDecoder by V8ValueDecoder(format, value) { - override val keys: List = value.blockingLock { + override val keys: List = value.evaluateInJSThreadBlocking(format.runtime) { keys.toList() } - override fun getElementAtIndex(index: Int): V8Value = value.getV8Value(getKeyAtIndex(index)) + override fun getElementAtIndex(index: Int): V8Value = value.getV8Value(format.runtime, getKeyAtIndex(index)) override fun decodeElement(descriptor: SerialDescriptor, index: Int): V8Value = - value.getV8Value(descriptor.getElementName(index)) + value.getV8Value(format.runtime, descriptor.getElementName(index)) override fun buildDecoderForSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy): V8ValueDecoder = when (index % 2 == 0) { true -> V8ValueDecoder(format, getKeyAtIndex(index).let(::V8Primitive)) @@ -62,11 +78,11 @@ internal class V8ObjectMapDecoder(override val format: J2V8Format, override val internal class V8ArrayListDecoder(override val format: J2V8Format, override val value: V8Array) : AbstractRuntimeArrayListDecoder(), NodeDecoder by V8ValueDecoder(format, value) { - override val keys: List = value.blockingLock { + override val keys: List = value.evaluateInJSThreadBlocking(format.runtime) { keys.map(String::toInt) } - override fun getElementAtIndex(index: Int): V8Value = value.getV8Value(getKeyAtIndex(index)) + override fun getElementAtIndex(index: Int): V8Value = value.getV8Value(format.runtime, getKeyAtIndex(index)) override fun buildDecoderForSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy): V8ValueDecoder = V8ValueDecoder(format, decodeElement(descriptor, index)) @@ -77,14 +93,14 @@ internal class V8ArrayListDecoder(override val format: J2V8Format, override val internal class V8ObjectClassDecoder(override val format: J2V8Format, override val value: V8Object) : AbstractRuntimeObjectClassDecoder(), NodeDecoder by V8ValueDecoder(format, value) { - override val keys: List = value.blockingLock { - keys.toList().filter { !value.getV8Value(it).isUndefined } + override val keys: List = value.evaluateInJSThreadBlocking(format.runtime) { + keys.toList().filter { !value.getV8Value(format.runtime, it).isUndefined } } - override fun getElementAtIndex(index: Int): V8Value = value.getV8Value(getKeyAtIndex(index)) + override fun getElementAtIndex(index: Int): V8Value = value.getV8Value(format.runtime, getKeyAtIndex(index)) override fun decodeElement(descriptor: SerialDescriptor, index: Int): V8Value = - value.getV8Value(descriptor.getElementName(index)) + value.getV8Value(format.runtime, descriptor.getElementName(index)) override fun buildDecoderForSerializableElement(descriptor: SerialDescriptor, index: Int, deserializer: DeserializationStrategy): V8ValueDecoder = V8ValueDecoder(format, decodeElement(descriptor, index)) @@ -110,6 +126,6 @@ internal class V8SealedClassDecoder(override val format: J2V8Format, override va } as? RuntimeClassDiscriminator )?.discriminator ?: format.config.discriminator - return value.getV8Value(discriminator).handleValue(format) + return value.getV8Value(format.runtime, discriminator).handleValue(format) } } diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Encoders.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Encoders.kt index 7b99b6b51..e1f22792f 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Encoders.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/V8Encoders.kt @@ -1,6 +1,10 @@ package com.intuit.player.jvm.j2v8.bridge.serialization.encoding -import com.eclipsesource.v8.* +import com.eclipsesource.v8.V8 +import com.eclipsesource.v8.V8Array +import com.eclipsesource.v8.V8Function +import com.eclipsesource.v8.V8Object +import com.eclipsesource.v8.V8Value import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper @@ -12,13 +16,18 @@ import com.intuit.player.jvm.core.bridge.serialization.json.value import com.intuit.player.jvm.core.bridge.serialization.serializers.FunctionLikeSerializer import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.core.bridge.serialization.serializers.ThrowableSerializer -import com.intuit.player.jvm.j2v8.* +import com.intuit.player.jvm.j2v8.V8Function +import com.intuit.player.jvm.j2v8.V8Null +import com.intuit.player.jvm.j2v8.V8Primitive +import com.intuit.player.jvm.j2v8.V8Value +import com.intuit.player.jvm.j2v8.addPrimitive import com.intuit.player.jvm.j2v8.bridge.V8Node import com.intuit.player.jvm.j2v8.bridge.V8ObjectWrapper import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8EncodingException import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8Format -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import com.intuit.player.jvm.j2v8.extensions.handleValue +import com.intuit.player.jvm.j2v8.pushPrimitive import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor @@ -55,30 +64,32 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m MAP, LIST, PRIMITIVE, - UNDECIDED + UNDECIDED, } private val currentContent get() = when (mode) { Mode.MAP -> contentMap Mode.LIST -> contentList Mode.PRIMITIVE, - Mode.UNDECIDED -> content + Mode.UNDECIDED, + -> content } private var content: V8Value = V8.getUndefined() get() = when (mode) { Mode.UNDECIDED, - Mode.PRIMITIVE -> field + Mode.PRIMITIVE, + -> field else -> error("cannot get content unless in PRIMITIVE mode") } - protected open val contentList = format.v8.blockingLock(::V8Array) + protected open val contentList = format.v8.evaluateInJSThreadBlocking(format.runtime) { V8Array(this) } get() = when (mode) { Mode.LIST -> field else -> error("cannot get list unless in LIST mode") } - protected open val contentMap = format.v8.blockingLock(::V8Object) + protected open val contentMap = format.v8.evaluateInJSThreadBlocking(format.runtime) { V8Object(this) } get() = when (mode) { Mode.MAP -> field else -> error("cannot get map unless in MAP mode") @@ -94,14 +105,15 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m else -> putContent(tag, content) } Mode.PRIMITIVE, - Mode.UNDECIDED -> { + Mode.UNDECIDED, + -> { this.content = content as? V8Value ?: error("cannot set content (${content?.let { it::class }}) unless wrapped as V8Value") endEncode() } } } - private operator fun V8Object.set(key: String, content: Any?): Unit = blockingLock { + private operator fun V8Object.set(key: String, content: Any?): Unit = evaluateInJSThreadBlocking(format.runtime) { when (content) { null -> addNull(key) Unit -> addUndefined(key) @@ -116,7 +128,7 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m } } - private fun V8Array.add(content: Any?): Unit = blockingLock { + private fun V8Array.add(content: Any?): Unit = evaluateInJSThreadBlocking(format.runtime) { when (content) { null -> pushNull() Unit -> pushUndefined() @@ -136,7 +148,8 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m Mode.LIST -> contentList.add(content) Mode.MAP -> contentMap[tag] = content Mode.UNDECIDED, - Mode.PRIMITIVE -> { + Mode.PRIMITIVE, + -> { this.content = content as? V8Value ?: error("cannot set content (${content?.let { it::class }}) unless wrapped as V8Value") endEncode() } @@ -145,22 +158,26 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m } override fun beginStructure( - descriptor: SerialDescriptor + descriptor: SerialDescriptor, ): CompositeEncoder { val consumer = when (mode) { Mode.LIST, - Mode.MAP -> { node -> putContent(node) } + Mode.MAP, + -> { node -> putContent(node) } Mode.PRIMITIVE, - Mode.UNDECIDED -> consumer + Mode.UNDECIDED, + -> consumer } - return if (descriptor == ThrowableSerializer().descriptor) + return if (descriptor == ThrowableSerializer().descriptor) { V8ExceptionEncoder(format, ::putContent) - else when (descriptor.kind) { - StructureKind.CLASS -> V8ValueEncoder(format, Mode.MAP, consumer) - StructureKind.LIST, is PolymorphicKind -> V8ValueEncoder(format, Mode.LIST, consumer) - StructureKind.MAP -> V8ValueEncoder(format, Mode.MAP, consumer) - else -> V8ValueEncoder(format, consumer) + } else { + when (descriptor.kind) { + StructureKind.CLASS -> V8ValueEncoder(format, Mode.MAP, consumer) + StructureKind.LIST, is PolymorphicKind -> V8ValueEncoder(format, Mode.LIST, consumer) + StructureKind.MAP -> V8ValueEncoder(format, Mode.MAP, consumer) + else -> V8ValueEncoder(format, consumer) + } } } @@ -172,7 +189,7 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m is V8Node -> value.v8Object is V8Value -> value else -> V8Value(value) - } + }, ) override fun encodeNull(): Unit = putContent(V8Null) @@ -208,7 +225,7 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m is JsonArray -> value.toList() is JsonPrimitive -> value.value else -> value - } as T + } as T, ) else -> super.encodeSerializableValue(serializer, value) @@ -222,7 +239,7 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m .toTypedArray() invokable(*encodedArgs) - } + }, ) /** @@ -251,15 +268,17 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m // check if type is nullable and value is null if (( - currValue == null && - // base type or type argument could be marked nullable - (kParam.type.isMarkedNullable || kParam.type.arguments[0].type?.isMarkedNullable == true) - ) || + currValue == null && + // base type or type argument could be marked nullable + (kParam.type.isMarkedNullable || kParam.type.arguments[0].type?.isMarkedNullable == true) + ) || // otherwise check if arg matches type if not null (currValue != null && currValue::class.isSubclassOf(kParam.type.arguments[0].type?.classifier as KClass<*>)) - ) + ) { index++ - else break + } else { + break + } } // only take matching args encodedArgs.slice(start until index).toTypedArray() @@ -272,7 +291,7 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m handleInvocation(kCallable::class, matchedArgs) { kCallable.call(*it) } - } + }, ) /** @@ -292,25 +311,29 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m * implementation of [KCallable.call] so that we * don't even need to use Java reflection. Until then... */ - override fun encodeFunction(function: Function<*>) = if (function is Invokable<*>) encodeFunction(function) else putContent( - V8Function(format) { args -> - val encodedArgs = (0 until args.length()) - .map { args[it].handleValue(format) } - - // Hate that we need to look at an internal class for arity - val arity = (function as kotlin.jvm.internal.FunctionBase<*>).arity - - // trim and pad args to fit arity constraints, - // note that padding will fail if arg types are non-nullable - val matchedArgs = (0 until arity) - .map { encodedArgs.getOrNull(it) } - .toTypedArray() - - handleInvocation(function::class, matchedArgs) { - function.invokeVararg(*it) - } - } - ) + override fun encodeFunction(function: Function<*>) = if (function is Invokable<*>) { + encodeFunction(function) + } else { + putContent( + V8Function(format) { args -> + val encodedArgs = (0 until args.length()) + .map { args[it].handleValue(format) } + + // Hate that we need to look at an internal class for arity + val arity = (function as kotlin.jvm.internal.FunctionBase<*>).arity + + // trim and pad args to fit arity constraints, + // note that padding will fail if arg types are non-nullable + val matchedArgs = (0 until arity) + .map { encodedArgs.getOrNull(it) } + .toTypedArray() + + handleInvocation(function::class, matchedArgs) { + function.invokeVararg(*it) + } + }, + ) + } private fun handleInvocation(reference: KClass<*>, args: Array, block: (Array) -> Any?) = try { block(args) @@ -326,7 +349,7 @@ internal open class V8ValueEncoder(private val format: J2V8Format, private val m internal class V8ExceptionEncoder(format: J2V8Format, consumer: (V8Value) -> Unit) : V8ValueEncoder(format, Mode.MAP, consumer) { override val contentMap by lazy { - format.v8.blockingLock { + format.v8.evaluateInJSThreadBlocking(format.runtime) { executeObjectScript("""(new Error())""") } } diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8Format.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8Format.kt index 8b4015a41..f4226dcd8 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8Format.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8Format.kt @@ -10,15 +10,18 @@ import com.intuit.player.jvm.j2v8.V8Value import com.intuit.player.jvm.j2v8.bridge.runtime.V8Runtime import com.intuit.player.jvm.j2v8.bridge.serialization.encoding.readV8 import com.intuit.player.jvm.j2v8.bridge.serialization.encoding.writeV8 -import com.intuit.player.jvm.j2v8.extensions.blockingLock -import kotlinx.serialization.* +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.modules.SerializersModule public class J2V8Format( config: J2V8FormatConfiguration, ) : AbstractRuntimeFormat(config) { - public val v8: V8 = (config.runtime as V8Runtime).v8 + public val v8: V8 by lazy { + (config.runtime as V8Runtime).v8 + } override fun encodeToRuntimeValue(serializer: SerializationStrategy, value: T): V8Value = writeV8(value, serializer) @@ -29,7 +32,7 @@ public class J2V8Format( public fun parseToV8Value(string: String): V8Value = parseToRuntimeValue(string) - override fun parseToRuntimeValue(string: String): V8Value = v8.blockingLock { + override fun parseToRuntimeValue(string: String): V8Value = v8.evaluateInJSThreadBlocking(runtime) { executeScript("($string)").let(::V8Value) } } diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/args.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Args.kt similarity index 100% rename from jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/args.kt rename to jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Args.kt diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/HandleValue.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/HandleValue.kt new file mode 100644 index 000000000..d086e33dc --- /dev/null +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/HandleValue.kt @@ -0,0 +1,77 @@ +package com.intuit.player.jvm.j2v8.extensions + +import com.eclipsesource.v8.V8 +import com.eclipsesource.v8.V8Array +import com.eclipsesource.v8.V8Function +import com.eclipsesource.v8.V8Object +import com.eclipsesource.v8.V8Value +import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.Invokable +import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat +import com.intuit.player.jvm.core.bridge.serialization.format.serializer +import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer +import com.intuit.player.jvm.j2v8.V8Primitive +import com.intuit.player.jvm.j2v8.bridge.V8Node +import com.intuit.player.jvm.j2v8.v8Array +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.builtins.ArraySerializer + +internal fun Any?.handleValue(format: RuntimeFormat): Any? = when (this) { + is V8Primitive -> value + is V8Value -> transform(format) + else -> this +} + +private fun V8Value.transform(format: RuntimeFormat): Any? = evaluateInJSThreadIfDefinedBlocking(format.runtime) { + when (this) { + V8.getUndefined() -> null + is V8Primitive -> value + is V8Function -> toInvokable(format, this, format.serializer()) + is V8Array -> toList(format) + is V8Object -> toNode(format) + else -> null + } +} + +internal fun V8Array.toList(format: RuntimeFormat): List? = evaluateInJSThreadIfDefinedBlocking(format.runtime) { + keys.map(::get).map { it.handleValue(format) } +} + +internal fun V8Object.toNode(format: RuntimeFormat): Node? = evaluateInJSThreadIfDefinedBlocking(format.runtime) { + if (contains("id") && contains("type")) { + Asset(V8Node(this, format.runtime)) + } else { + V8Node(this, format.runtime) + } +} + +internal fun V8Function.toInvokable(format: RuntimeFormat, receiver: V8Object, deserializationStrategy: DeserializationStrategy?): Invokable? = evaluateInJSThreadIfDefinedBlocking(format.runtime) { + Invokable { args -> + evaluateInJSThreadBlocking(format.runtime) { + try { + when ( + val result = + call( + receiver, + format.encodeToRuntimeValue( + ArraySerializer(GenericSerializer()), + args as Array, + ).v8Array, + ).handleValue(format) + ) { + is Node -> deserializationStrategy?.let { + result.deserialize(deserializationStrategy) + } ?: run { + result as R + } + + else -> result as R + } + } catch (e: Throwable) { + e.printStackTrace() + throw e + } + } + } +} diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/invoke.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Invoke.kt similarity index 67% rename from jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/invoke.kt rename to jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Invoke.kt index b7f076171..f93e2957f 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/invoke.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Invoke.kt @@ -4,8 +4,8 @@ import com.eclipsesource.v8.V8Array import com.eclipsesource.v8.V8Function import com.intuit.player.jvm.j2v8.bridge.serialization.format.J2V8Format -internal operator fun V8Function.invoke(): Any? = - call(runtime, runtime.blockingLock(::V8Array)) +internal operator fun V8Function.invoke(format: J2V8Format): Any? = + call(runtime, runtime.evaluateInJSThreadBlocking(format.runtime) { V8Array(this) }) internal operator fun V8Function.invoke(format: J2V8Format, vararg args: Any?): Any? = call(runtime, format.args(*args)) diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Lock.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Lock.kt new file mode 100644 index 000000000..e73fd6f7e --- /dev/null +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Lock.kt @@ -0,0 +1,50 @@ +package com.intuit.player.jvm.j2v8.extensions + +import com.eclipsesource.v8.V8Value +import com.intuit.player.jvm.core.bridge.PlayerRuntimeException +import com.intuit.player.jvm.core.bridge.runtime.Runtime +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout + +internal suspend fun Context.evaluateInJSThread( + runtime: Runtime, + timeout: Long = 5000, + block: suspend Context.() -> T, +): T = withTimeout(timeout) { + if (runtime.isReleased()) throw PlayerRuntimeException(runtime, "Runtime object has been released!") + withContext(runtime.dispatcher) { + runtime.scope.ensureActive() + block() + } +} + +internal fun Context.evaluateInJSThreadBlocking( + runtime: Runtime, + timeout: Long = 5000, + block: Context.() -> T, +): T { + if (runtime.isReleased()) throw PlayerRuntimeException(runtime, "Runtime object has been released!") + // if we're already on the dispatcher thread, DON'T BLOCK + return if (this@evaluateInJSThreadBlocking.runtime.locker.hasLock()) { + block() + } else { + runtime.checkBlockingThread(Thread.currentThread()) + runBlocking { + evaluateInJSThread(runtime, timeout, block) + } + } +} + +internal suspend fun Context.evaluateInJSThreadIfDefined( + runtime: Runtime, + timeout: Long = 5000, + block: suspend Context.() -> T, +): T? = mapUndefinedToNull()?.let { evaluateInJSThread(runtime, timeout, block) } + +internal fun Context.evaluateInJSThreadIfDefinedBlocking( + runtime: Runtime, + timeout: Long = 5000, + block: Context.() -> T, +): T? = mapUndefinedToNull()?.let { evaluateInJSThreadBlocking(runtime, timeout, block) } diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/map.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Map.kt similarity index 100% rename from jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/map.kt rename to jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Map.kt diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/unlock.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Unlock.kt similarity index 100% rename from jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/unlock.kt rename to jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Unlock.kt diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/unwrap.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Unwrap.kt similarity index 100% rename from jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/unwrap.kt rename to jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/Unwrap.kt diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/handleValue.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/handleValue.kt deleted file mode 100644 index 628b600fc..000000000 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/handleValue.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.intuit.player.jvm.j2v8.extensions - -import com.eclipsesource.v8.* -import com.intuit.player.jvm.core.asset.Asset -import com.intuit.player.jvm.core.bridge.Invokable -import com.intuit.player.jvm.core.bridge.Node -import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat -import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer -import com.intuit.player.jvm.j2v8.V8Primitive -import com.intuit.player.jvm.j2v8.bridge.V8Node -import com.intuit.player.jvm.j2v8.v8Array -import kotlinx.serialization.builtins.ArraySerializer - -internal fun Any?.handleValue(format: RuntimeFormat): Any? = when (this) { - is V8Primitive -> value - is V8Value -> transform(format) - else -> this -} - -private fun V8Value.transform(format: RuntimeFormat): Any? = lockIfDefined { - when (this) { - V8.getUndefined() -> null - is V8Primitive -> value - is V8Function -> toInvokable(format, this) - is V8Array -> toList(format) - is V8Object -> toNode(format) - else -> null - } -} - -internal fun V8Array.toList(format: RuntimeFormat): List? = if (isUndefined) null else lockIfDefined { - keys.map(::get).map { it.handleValue(format) } -} - -internal fun V8Object.toNode(format: RuntimeFormat): Node? = if (isUndefined) null else lockIfDefined { - if (contains("id") && contains("type")) - Asset(V8Node(this, format.runtime)) - else V8Node(this, format.runtime) -} - -internal fun V8Function.toInvokable(format: RuntimeFormat, receiver: V8Object): Invokable? = if (isUndefined) null else lockIfDefined { - Invokable { args -> - blockingLock { - try { - call( - receiver, - format.encodeToRuntimeValue( - ArraySerializer(GenericSerializer()), - args as Array - ).v8Array - ).handleValue(format) as R - } catch (e: Throwable) { - e.printStackTrace() - throw e - } - } - } -} diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/lock.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/lock.kt deleted file mode 100644 index b8d4041c0..000000000 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/extensions/lock.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.intuit.player.jvm.j2v8.extensions - -import com.eclipsesource.v8.V8Value -import com.intuit.player.jvm.j2v8.bridge.runtime.PlayerRuntimeException -import kotlinx.coroutines.delay -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout - -/** - * Wait for lock and execute [block]. After execution, or on exception, the lock will be released - * unless the lock was already acquired within the calling context. To prevent deadlock, this - * lock will only wait for [timeout] milliseconds, defaulting to 5000. - * - * NOTE: Suspend methods are non-blocking by convention. - */ -internal suspend fun Context.lock(timeout: Long = 5000, block: suspend Context.() -> T): T = withTimeout(timeout) { - if (isReleased) throw PlayerRuntimeException(runtime, "Runtime object has been released!") - val alreadyHadLock = runtime.locker.hasLock() - try { - while (!runtime.locker.tryAcquire()) delay(50) - block() - } finally { - if (!alreadyHadLock && runtime.locker.hasLock()) runtime.locker.release() - } -} - -/** - * Blocking method to execute [block]. If the calling context does not have the lock - * then the lock will attempt to be acquired through delegation to [lock]. This - * will continue to block the calling thread. - */ -internal fun Context.blockingLock(block: Context.() -> T): T = when { - isReleased -> throw PlayerRuntimeException(runtime, "Runtime object has been released!") - runtime.locker.hasLock() -> block() - else -> runBlocking { - lock { block() } - } -} - -/** Special [blockingLock] helper to guard against undefined values */ -internal fun Context.lockIfDefined( - block: Context.() -> T -): T? = mapUndefinedToNull()?.let { blockingLock(block) } diff --git a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/plugins/V8ScriptPlayerPlugin.kt b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/plugins/V8ScriptPlayerPlugin.kt index bc2d9ef5d..9cc849811 100644 --- a/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/plugins/V8ScriptPlayerPlugin.kt +++ b/jvm/j2v8/src/main/kotlin/com/intuit/player/jvm/j2v8/plugins/V8ScriptPlayerPlugin.kt @@ -5,16 +5,16 @@ import com.intuit.player.jvm.core.plugins.JSScriptPluginWrapper @Deprecated( "Replaced with more generic JSScriptPluginWrapper", ReplaceWith("JSScriptPluginWrapper"), - DeprecationLevel.HIDDEN + DeprecationLevel.HIDDEN, ) public typealias V8ScriptPlayerPlugin = JSScriptPluginWrapper @Deprecated( "Replaced with more generic V8ScriptPluginWrapper", ReplaceWith("JSScriptPluginWrapper"), - DeprecationLevel.HIDDEN + DeprecationLevel.HIDDEN, ) -public abstract class V8ScriptPluginWrapper(name: String, script: String) : JSScriptPluginWrapper(name, script) { +public abstract class V8ScriptPluginWrapper(name: String, script: String) : JSScriptPluginWrapper(name, script = script) { public constructor(name: String, sourcePath: String, classLoader: ClassLoader = JSScriptPluginWrapper::class.java.classLoader!!) : this(name, classLoader.getResource(sourcePath)!!.readText()) } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/AutoAcquireJ2V8Test.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/AutoAcquireJ2V8Test.kt deleted file mode 100644 index d3e7335b9..000000000 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/AutoAcquireJ2V8Test.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.intuit.player.jvm.j2v8.base - -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeEach - -internal abstract class AutoAcquireJ2V8Test : J2V8Test() { - - @BeforeEach - fun acquire() { - v8.locker.acquire() - } - - @AfterEach - fun release() { - v8.locker.release() - } -} diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/J2V8Test.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/J2V8Test.kt index 1e9612dfb..9e3cb956b 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/J2V8Test.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/base/J2V8Test.kt @@ -10,14 +10,17 @@ import com.intuit.player.jvm.j2v8.bridge.runtime.Runtime import com.intuit.player.jvm.j2v8.bridge.runtime.V8Runtime import com.intuit.player.jvm.j2v8.bridge.serialization.format.decodeFromV8Value import com.intuit.player.jvm.j2v8.bridge.serialization.format.encodeToV8Value -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadIfDefinedBlocking import com.intuit.player.jvm.j2v8.extensions.unlock import com.intuit.player.jvm.j2v8.v8Object import com.intuit.player.jvm.utils.test.PromiseUtils import com.intuit.player.jvm.utils.test.ThreadUtils import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.json.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.buildJsonObject import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach @@ -32,8 +35,8 @@ internal abstract class J2V8Test(val v8: V8 = V8.createV8Runtime().unlock()) : P fun buildV8Object(jsonElement: JsonElement = buildJsonObject {}) = buildV8ObjectFromMap( Json.decodeFromJsonElement( MapSerializer(String.serializer(), GenericSerializer()), - jsonElement - ) + jsonElement, + ), ) fun buildV8ObjectFromMap(map: Map): V8Object = format.encodeToV8Value(map).v8Object @@ -49,7 +52,7 @@ internal abstract class J2V8Test(val v8: V8 = V8.createV8Runtime().unlock()) : P @BeforeEach fun resetGlobalLog() { - v8.blockingLock { + v8.evaluateInJSThreadIfDefinedBlocking(runtime) { val globalLog = V8Array(this) add("globalLog", globalLog) globalLog.close() @@ -57,7 +60,7 @@ internal abstract class J2V8Test(val v8: V8 = V8.createV8Runtime().unlock()) : P } fun flushRuntimeLogs() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val globalLog = getArray("globalLog") globalLog.keys.map { globalLog.get(it) }.forEach { it.prettyPrint() } globalLog.close() @@ -71,8 +74,8 @@ internal abstract class J2V8Test(val v8: V8 = V8.createV8Runtime().unlock()) : P fun V8Object.assertEquivalent(another: Any?) { assertTrue( another is V8Object, - "value to compare is not a V8Object: $another" - ) + ) { "value to compare is not a V8Object: $another" } + (another as V8Object).let { // verify that all missing keys from another are null or undefined (keys.toSet() - another.keys.toSet()).forEach { missingKey -> @@ -88,12 +91,14 @@ internal abstract class J2V8Test(val v8: V8 = V8.createV8Runtime().unlock()) : P if (isUndefined) { assertEquals(this, another) - } else keys.forEach { key -> - val (expected, actual) = get(key) to another.get(key) - if (expected is V8Object && !expected.isUndefined) { - expected.assertEquivalent(actual) - } else { - assertEquals(expected, actual, "comparing key: $key") + } else { + keys.forEach { key -> + val (expected, actual) = get(key) to another.get(key) + if (expected is V8Object && !expected.isUndefined) { + expected.assertEquivalent(actual) + } else { + assertEquals(expected, actual, "comparing key: $key") + } } } } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/V8NodeTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/V8NodeTest.kt index 2f2d50f18..43632a2e5 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/V8NodeTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/V8NodeTest.kt @@ -3,12 +3,13 @@ package com.intuit.player.jvm.j2v8.bridge import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.getJson import com.intuit.player.jvm.core.bridge.toJson import com.intuit.player.jvm.core.flow.Flow import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.j2v8.bridge.serialization.format.v8Object -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import com.intuit.player.jvm.j2v8.extensions.handleValue import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking @@ -16,7 +17,9 @@ import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.JsonNull import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.put -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Test import kotlin.concurrent.thread @@ -24,23 +27,23 @@ import kotlin.concurrent.thread internal class V8NodeTest : J2V8Test() { @Test - fun get() = v8.blockingLock { + fun get() = v8.evaluateInJSThreadBlocking(runtime) { val node = buildNodeFromMap( "string" to "thisisastring", "int" to 1, "object" to mapOf( - "string" to "anotherstring" + "string" to "anotherstring", ), "list" to listOf( 1, "two", mapOf( - "string" to "onemorestring" + "string" to "onemorestring", ), - null + null, ), "function" to Invokable { "classicstring" }, - "null" to null + "null" to null, ) assertEquals("thisisastring", node["string"]) @@ -58,7 +61,7 @@ internal class V8NodeTest : J2V8Test() { fun getString() { val node = buildNodeFromMap( "string" to "string", - "notastring" to 1 + "notastring" to 1, ) assertEquals("string", node.getString("string")) @@ -67,24 +70,24 @@ internal class V8NodeTest : J2V8Test() { } @Test - fun getFunction() = v8.blockingLock { + fun getFunction() = v8.evaluateInJSThreadBlocking(runtime) { val node = buildNodeFromMap( "function" to Invokable { "classicstring" }, "tuple" to Invokable { (p0, p1) -> listOf(p0, p1) }, - "notafunction" to 1 + "notafunction" to 1, ) - assertEquals("classicstring", node.getFunction("function")?.invoke()) - assertEquals(listOf("1", 2), node.getFunction("tuple")?.invoke("1", 2)) - assertEquals(null, node.getFunction("notafunction")) - assertEquals(null, node.getFunction("notthere")) + assertEquals("classicstring", node.getInvokable("function")?.invoke()) + assertEquals(listOf("1", 2), node.getInvokable("tuple")?.invoke("1", 2)) + assertEquals(null, node.getInvokable("notafunction")) + assertEquals(null, node.getInvokable("notthere")) } @Test fun getList() { val node = buildNodeFromMap( "list" to listOf(1, 2, 3), - "notalist" to 1 + "notalist" to 1, ) assertEquals(listOf(1, 2, 3), node.getList("list")) @@ -96,9 +99,9 @@ internal class V8NodeTest : J2V8Test() { fun getObject() { val node = buildNodeFromMap( "object" to mapOf( - "string" to "thisisastring" + "string" to "thisisastring", ), - "notaobject" to 1234 + "notaobject" to 1234, ) assertEquals("thisisastring", node.getObject("object")?.getString("string")) @@ -112,8 +115,8 @@ internal class V8NodeTest : J2V8Test() { val node = buildNodeFromMap( "asset" to mapOf( "id" to "testId", - "type" to "testType" - ) + "type" to "testType", + ), ) val (id, type) = node.getObject("asset") as Asset @@ -127,14 +130,14 @@ internal class V8NodeTest : J2V8Test() { "assets" to listOf( mapOf( "id" to "testId1", - "type" to "testType" + "type" to "testType", ), mapOf( - "id" to "notAnAsset" + "id" to "notAnAsset", ), - 1 + 1, ), - "notassets" to "justastring" + "notassets" to "justastring", ) val assets = node.getList("assets") as List<*> @@ -156,7 +159,7 @@ internal class V8NodeTest : J2V8Test() { fun getInt() { val node = buildNodeFromMap( "int" to 1, - "notanint" to "asdf" + "notanint" to "asdf", ) assertEquals(1, node.getInt("int")) @@ -167,7 +170,7 @@ internal class V8NodeTest : J2V8Test() { @Test fun getJson() { val node = buildNodeFromMap( - "beacon" to mapOf("key" to "value") + "beacon" to mapOf("key" to "value"), ) assertEquals(JsonNull, node.getJson("notthere")) assertEquals(buildJsonObject { put("key", "value") }, node.getJson("beacon")) @@ -187,17 +190,17 @@ internal class V8NodeTest : J2V8Test() { "beacon", buildJsonObject { put("key", "value") - } + }, ) }, - node.toJson() + node.toJson(), ) } @Test fun getBoolean() { val node = buildNodeFromMap( - "isSelected" to true + "isSelected" to true, ) assertEquals(true, node.getBoolean("isSelected")) assertNull(node.getBoolean("notthere")) @@ -209,51 +212,51 @@ internal class V8NodeTest : J2V8Test() { "string" to "thisisastring", "int" to 1, "object" to mapOf( - "string" to "anotherstring" + "string" to "anotherstring", ), "list" to listOf( 1, "two", mapOf( - "string" to "onemorestring" + "string" to "onemorestring", ), - null + null, ), "function" to Invokable { "classicstring" }, - "null" to null + "null" to null, ) addThreads( thread { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { runBlocking { delay(1000) } assertEquals(1, node.getInt("int")) } }, thread(false) { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { assertEquals("thisisastring", node["string"]) } - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { assertEquals("thisisastring", node.getString("string")) } - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { assertEquals(1, node.getInt("int")) } assertEquals("thisisastring", node.get("string")) }, thread(false) { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { assertEquals("thisisastring", node["string"]) } - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { assertEquals("thisisastring", node.getString("string")) } - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { assertEquals(1, node.getInt("int")) } assertEquals("thisisastring", node.get("string")) - } + }, ) startThreads() verifyThreads() @@ -262,7 +265,7 @@ internal class V8NodeTest : J2V8Test() { @Test fun getSerializablePrimitive() { val node = buildNodeFromMap( - "number" to 9 + "number" to 9, ) assertEquals(9, node.getSerializable("number", Int.serializer())) } @@ -271,8 +274,8 @@ internal class V8NodeTest : J2V8Test() { fun getSerializable() { val node = buildNodeFromMap( "flow" to mapOf( - "id" to "testId" - ) + "id" to "testId", + ), ) assertEquals("testId", node.getSerializable("flow", Flow.serializer())?.id) } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8DecoderTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8DecoderTest.kt index 1ea995f29..4214556f3 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8DecoderTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8DecoderTest.kt @@ -1,17 +1,19 @@ package com.intuit.player.jvm.j2v8.bridge.serialization -import com.eclipsesource.v8.* +import com.eclipsesource.v8.V8 +import com.eclipsesource.v8.V8Object +import com.eclipsesource.v8.V8ScriptExecutionException import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.serialization.json.prettify import com.intuit.player.jvm.core.bridge.serialization.json.prettyPrint import com.intuit.player.jvm.j2v8.V8Function import com.intuit.player.jvm.j2v8.V8Value -import com.intuit.player.jvm.j2v8.base.AutoAcquireJ2V8Test +import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.j2v8.bridge.serialization.format.decodeFromV8Value import com.intuit.player.jvm.j2v8.bridge.serialization.format.encodeToV8Value import com.intuit.player.jvm.j2v8.extensions.args -import com.intuit.player.jvm.j2v8.extensions.blockingLock import com.intuit.player.jvm.j2v8.extensions.emptyArgs +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import com.intuit.player.jvm.j2v8.extensions.invoke import com.intuit.player.jvm.j2v8.v8Function import com.intuit.player.jvm.j2v8.v8Object @@ -22,13 +24,13 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test /** Legacy tests for encoding values into J2V8 */ -internal class V8DecoderTest : AutoAcquireJ2V8Test() { +internal class V8DecoderTest : J2V8Test() { private val retVal = "this is my return" @Test fun testPrimitives() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { listOf(42, 1234L, "string", null, Unit to V8.getUndefined()) .map { if (it is Pair<*, *>) it else it to V8Value(it) } .forEach { (actual, expected) -> assertEquals(expected, format.encodeToV8Value(actual)) } @@ -37,7 +39,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testArray() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val list = listOf(1, 2, 3) val v8Array = executeArrayScript("""(${list.prettify()})""") val result = format.encodeToV8Value(list) @@ -47,7 +49,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testNestedArray() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val list = listOf("a", 2, 3, listOf(4, 5, 6)) val nestedV8Array = executeArrayScript("""(${list.prettify()})""") val result = format.encodeToV8Value(list) @@ -57,7 +59,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testObject() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf("a" to "b", "c" to "d") val v8Object = executeObjectScript("""(${map.prettify()})""") val result = format.encodeToV8Value(map) @@ -67,7 +69,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testObjectDynamicKeys() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf(1 to "b", "c" to "d") val v8Object = executeObjectScript("""(${map.prettify()})""") val result = format.encodeToV8Value(map) @@ -77,7 +79,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testNestedObject() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf("a" to "b", "c" to "d") val v8Object = executeObjectScript("""(${map.prettify()})""") val result = format.encodeToV8Value(map) @@ -87,7 +89,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val function = { arg: String -> println(arg); retVal } assertEquals(retVal, function("this is my arg")) @@ -98,7 +100,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testFunctionReturn() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val function = { arg: String -> arg } assertEquals("this is my arg", function("this is my arg")) @@ -109,7 +111,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testTooManyParamsOnFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val function = { arg: String -> println(arg); retVal } assertEquals(retVal, function("this is my arg")) @@ -120,7 +122,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun notEnoughParamsOnFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val function = { arg: String? -> println(arg); retVal } assertEquals(retVal, function("this is my arg")) @@ -131,7 +133,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun wrongParamsOnFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val function = { arg: String -> println(arg); retVal } assertEquals(retVal, function("this is my arg")) @@ -144,7 +146,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testMemberFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val logger = LoggerAsMethod() val function = logger::log assertEquals(retVal, function("this is my arg")) @@ -156,7 +158,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testMemberFunctionReturn() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val logger = LoggerAsMethod() val function = logger::logAndReturn assertEquals("this is my arg", function("this is my arg")) @@ -168,7 +170,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testTooManyParamsOnMemberFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val logger = LoggerAsMethod() val function = logger::log assertEquals(retVal, function("this is my arg")) @@ -180,19 +182,19 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun notEnoughParamsOnMemberFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val logger = LoggerAsMethod() val function = logger::log assertEquals(retVal, function("this is my arg")) val result = format.encodeToV8Value(function).v8Function - assertEquals(retVal, result()) + assertEquals(retVal, result(format)) } } @Test fun wrongParamsOnMemberFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val logger = LoggerAsMethod() val function = logger::log assertEquals(retVal, function("this is my arg")) @@ -215,7 +217,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { } } - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val logger = ComplicatedVarargMethod() val function = logger::log // assertEquals(retVal, function("someArg", argsAsList.toTypedArray(), 42)) @@ -228,12 +230,12 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testComplexStructure() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val complex = mapOf( "string" to "thisisastring", "int" to 1, "object" to mapOf( - "string" to "anotherstring" + "string" to "anotherstring", ), "list" to listOf( 1, @@ -241,14 +243,14 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { listOf( "a", "b", - "c" + "c", ), mapOf( - "string" to "onemorestring" + "string" to "onemorestring", ), - null + null, ), - "null" to null + "null" to null, ) val v8Object = executeObjectScript("""(${complex.prettify()})""") val result = format.encodeToV8Value(complex) @@ -264,7 +266,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testV8ValueDecoding() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val testClass = TestClass1(1, "string") val obj = executeObjectScript("""(${testClass.prettify(TestClass1.serializer())})""") @@ -274,7 +276,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { val mapTestClass = mapOf( "one" to 1, - "string" to "string" + "string" to "string", ) val encodedMapTestClass = format.decodeFromV8Value(obj) @@ -300,7 +302,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testV8ValueDecodingWithFunctionType() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val testClass = TestClass2(1, "string") { println("$it called me!") return@TestClass2 true @@ -311,7 +313,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { V8Function(format) { println("$it called me!") true - } + }, ) val obj = executeObjectScript("""({one: 1, string: "string", method: f})""") val decodedObj = format.encodeToV8Value(testClass).v8Object @@ -351,18 +353,22 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Test fun testV8ValueDecodingWithInvokableType() { - v8.blockingLock { - val testClass = TestClass3(1, "string") { - println("${it.firstOrNull()} called me!") - return@TestClass3 true - } + v8.evaluateInJSThreadBlocking(runtime) { + val testClass = TestClass3( + 1, + "string", + Invokable { + println("${it.firstOrNull()} called me!") + return@Invokable true + }, + ) add( "f", V8Function(format) { println("$it called me!") true - } + }, ) val obj = executeObjectScript("""({one: 1, string: "string", method: f})""") val decodedObj = format.encodeToV8Value(testClass) as V8Object @@ -398,12 +404,12 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { data class TestClass4( val one: Int, val string: String, - val nested: TestClass4? = null + val nested: TestClass4? = null, ) @Test fun testV8ValueDecodingWithNestedDataClass() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val testClass = TestClass4(1, "string", TestClass4(2, "another")) testClass.prettyPrint(TestClass4.serializer()) @@ -418,8 +424,8 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { "string" to "string", "nested" to mapOf( "one" to 2, - "string" to "another" - ) + "string" to "another", + ), ) val encodedMapTestClass = format.decodeFromV8Value>(obj) @@ -440,18 +446,18 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { data class TestClass5( val one: Int, val string: String, - val nested: TestClass6 + val nested: TestClass6, ) @Serializable data class TestClass6( val two: String, - val string: Int + val string: Int, ) @Test fun testV8ValueDecodingWithComplexDataClass() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val testClass = TestClass5(1, "string", TestClass6("another", 2)) testClass.prettyPrint(TestClass5.serializer()) @@ -466,8 +472,8 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { "string" to "string", "nested" to mapOf( "two" to "another", - "string" to 2 - ) + "string" to 2, + ), ) val encodedMapTestClass = format.decodeFromV8Value>(obj) @@ -486,7 +492,7 @@ internal class V8DecoderTest : AutoAcquireJ2V8Test() { @Serializable class LoggerAsValue( - var TAG: String = "Logger As Value" + var TAG: String = "Logger As Value", ) { private val retVal = "this is my return" val log: ((String?) -> String)? = { println(TAG); println(it); retVal } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8EncoderTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8EncoderTest.kt index 36d130ca8..297df8009 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8EncoderTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/V8EncoderTest.kt @@ -4,19 +4,19 @@ import com.eclipsesource.v8.V8Function import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.serialization.json.prettify import com.intuit.player.jvm.j2v8.V8Function -import com.intuit.player.jvm.j2v8.base.AutoAcquireJ2V8Test +import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.j2v8.bridge.serialization.format.decodeFromV8Value -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import com.intuit.player.jvm.j2v8.extensions.invoke import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test /** Legacy tests for decoding values from J2V8 */ -internal class V8EncoderTest : AutoAcquireJ2V8Test() { +internal class V8EncoderTest : J2V8Test() { @Test fun testArray() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val list = listOf(1, 2, 3) val v8Array = executeArrayScript("""(${list.prettify()})""") val result = format.decodeFromV8Value>(v8Array) @@ -26,7 +26,7 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { @Test fun testNestedArray() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val list = listOf("a", 2, 3, listOf(4, 5, 6)) val nestedV8Array = executeArrayScript("""(${list.prettify()})""") val result: List = format.decodeFromV8Value(nestedV8Array) @@ -36,7 +36,7 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { @Test fun testObject() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf("a" to "b", "c" to "d") val v8Object = executeObjectScript("""(${map.prettify()})""") val result = format.decodeFromV8Value>(v8Object) @@ -46,7 +46,7 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { @Test fun testObjectDynamicKeysAreConvertedToStrings() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf(1 to "b", "c" to "d") val v8Object = executeObjectScript("""(${map.prettify()})""") val result = format.decodeFromV8Value>(v8Object) @@ -56,7 +56,7 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { @Test fun testNestedObject() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf("a" to mapOf("b" to "c"), "d" to mapOf("e" to "f")) val v8Object = executeObjectScript("""(${map.prettify()})""") val result = format.decodeFromV8Value>>(v8Object) @@ -66,7 +66,7 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { @Test fun testFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val retVal = "this is my return" val v8Function = V8Function(format) { args -> println(args.get(0)); retVal @@ -80,13 +80,13 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { @Test fun testContainedFunction() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val retVal = "this is my return" add( "func", V8Function(format) { args -> println(args.get(0)); retVal - } + }, ) val functionContainer = executeObjectScript("""({ log: func })""") val v8Function = functionContainer.getObject("log") as? V8Function @@ -101,12 +101,12 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { @Test fun testComplexStructure() { - v8.blockingLock { + v8.evaluateInJSThreadBlocking(runtime) { val complex = mapOf( "string" to "thisisastring", "int" to 1, "object" to mapOf( - "string" to "anotherstring" + "string" to "anotherstring", ), "list" to listOf( 1, @@ -114,14 +114,14 @@ internal class V8EncoderTest : AutoAcquireJ2V8Test() { listOf( "a", "b", - "c" + "c", ), mapOf( - "string" to "onemorestring" + "string" to "onemorestring", ), - null + null, ), - "null" to null + "null" to null, ) val v8Object = executeObjectScript("""(${complex.prettify()})""") val result: Any? = format.decodeFromV8Value(v8Object) diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/DecodingTests.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/DecodingTests.kt index d87b771ab..500f6cae4 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/DecodingTests.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/DecodingTests.kt @@ -2,10 +2,14 @@ package com.intuit.player.jvm.j2v8.bridge.serialization.encoding import com.eclipsesource.v8.V8 import com.intuit.player.jvm.core.bridge.Invokable -import com.intuit.player.jvm.j2v8.* +import com.intuit.player.jvm.j2v8.V8Array +import com.intuit.player.jvm.j2v8.V8Function +import com.intuit.player.jvm.j2v8.V8Null +import com.intuit.player.jvm.j2v8.V8Object +import com.intuit.player.jvm.j2v8.V8Primitive import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.j2v8.bridge.serialization.format.decodeFromV8Value -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import kotlinx.serialization.Serializable import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test @@ -13,39 +17,39 @@ import org.junit.jupiter.api.Test internal class PrimitiveDecoding : J2V8Test() { @Test - fun `decode string primitive`() = v8.blockingLock { + fun `decode string primitive`() = v8.evaluateInJSThreadBlocking(runtime) { assertEquals("hello", format.decodeFromV8Value(V8Primitive("hello"))) } @Test - fun `decode boolean primitive`() = v8.blockingLock { + fun `decode boolean primitive`() = v8.evaluateInJSThreadBlocking(runtime) { assertEquals(true, format.decodeFromV8Value(V8Primitive(true))) } @Test - fun `decode int primitive`() = v8.blockingLock { + fun `decode int primitive`() = v8.evaluateInJSThreadBlocking(runtime) { assertEquals(20, format.decodeFromV8Value(V8Primitive(20))) } @Test - fun `decode double primitive`() = v8.blockingLock { + fun `decode double primitive`() = v8.evaluateInJSThreadBlocking(runtime) { assertEquals(2.2, format.decodeFromV8Value(V8Primitive(2.2))) } @Test - fun `decode unit`() = v8.blockingLock { + fun `decode unit`() = v8.evaluateInJSThreadBlocking(runtime) { assertEquals(null, format.decodeFromV8Value(V8.getUndefined())) } @Test - fun `decode null`() = v8.blockingLock { + fun `decode null`() = v8.evaluateInJSThreadBlocking(runtime) { assertEquals(null, format.decodeFromV8Value(V8Null)) } } internal class FunctionDecoding : J2V8Test() { - @Test fun `decode typed lambda`() = v8.blockingLock { + @Test fun `decode typed lambda`() = v8.evaluateInJSThreadBlocking(runtime) { val args = V8Array { push("PLAYER") push(1) @@ -60,11 +64,11 @@ internal class FunctionDecoding : J2V8Test() { assertEquals("PLAYER: 1", function.call(this, args)) assertEquals( "PLAYER: 2", - format.decodeFromV8Value>(function)("PLAYER", 2) + format.decodeFromV8Value>(function)("PLAYER", 2), ) } - @Test fun `decode invokable`() = v8.blockingLock { + @Test fun `decode invokable`() = v8.evaluateInJSThreadBlocking(runtime) { val args = V8Array { push("PLAYER") push(1) @@ -79,14 +83,14 @@ internal class FunctionDecoding : J2V8Test() { assertEquals("PLAYER: 1", function.call(this, args)) assertEquals( "PLAYER: 2", - format.decodeFromV8Value>(function)("PLAYER", 2) + format.decodeFromV8Value>(function)("PLAYER", 2), ) } - @Test fun `decode kcallable`() = v8.blockingLock { + @Test fun `decode kcallable`() = v8.evaluateInJSThreadBlocking(runtime) { @Serializable data class Container( - val method: (String, Int) -> String + val method: (String, Int) -> String, ) val args = V8Array { @@ -106,8 +110,8 @@ internal class FunctionDecoding : J2V8Test() { format.decodeFromV8Value( V8Object { add("method", function) - } - ).method("PLAYER", 2) + }, + ).method("PLAYER", 2), ) } } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/EncodingTests.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/EncodingTests.kt index d80a04a75..7151d7864 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/EncodingTests.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/EncodingTests.kt @@ -2,61 +2,64 @@ package com.intuit.player.jvm.j2v8.bridge.serialization.encoding import com.eclipsesource.v8.V8 import com.intuit.player.jvm.core.bridge.Invokable -import com.intuit.player.jvm.j2v8.* +import com.intuit.player.jvm.j2v8.V8Array +import com.intuit.player.jvm.j2v8.V8Null +import com.intuit.player.jvm.j2v8.V8Primitive import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.j2v8.bridge.serialization.format.encodeToV8Value -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking +import com.intuit.player.jvm.j2v8.v8Function import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test internal class PrimitiveEncoding : J2V8Test() { - @Test fun `encode string primitive`() = format.v8.blockingLock { + @Test fun `encode string primitive`() = format.v8.evaluateInJSThreadBlocking(runtime) { assertEquals(V8Primitive("hello"), format.encodeToV8Value("hello")) } - @Test fun `encode boolean primitive`() = format.v8.blockingLock { + @Test fun `encode boolean primitive`() = format.v8.evaluateInJSThreadBlocking(runtime) { assertEquals(V8Primitive(true), format.encodeToV8Value(true)) } - @Test fun `encode int primitive`() = format.v8.blockingLock { + @Test fun `encode int primitive`() = format.v8.evaluateInJSThreadBlocking(runtime) { assertEquals(V8Primitive(20), format.encodeToV8Value(20)) } - @Test fun `encode double primitive`() = format.v8.blockingLock { + @Test fun `encode double primitive`() = format.v8.evaluateInJSThreadBlocking(runtime) { assertEquals(V8Primitive(2.2), format.encodeToV8Value(2.2)) } - @Test fun `encode long primitive`() = format.v8.blockingLock { + @Test fun `encode long primitive`() = format.v8.evaluateInJSThreadBlocking(runtime) { assertEquals(V8Primitive(20.0), format.encodeToV8Value(20L)) } - @Test fun `encode unit`() = format.v8.blockingLock { + @Test fun `encode unit`() = format.v8.evaluateInJSThreadBlocking(runtime) { assertEquals(V8.getUndefined(), format.encodeToV8Value(Unit)) } - @Test fun `encode null`() = format.v8.blockingLock { + @Test fun `encode null`() = format.v8.evaluateInJSThreadBlocking(runtime) { assertEquals(V8Null, format.encodeToV8Value(null)) } } internal class FunctionEncoding : J2V8Test() { - @Test fun `encode typed lambda`() = v8.blockingLock { + @Test fun `encode typed lambda`() = v8.evaluateInJSThreadBlocking(runtime) { val callback = { p0: String, p1: Int -> "$p0: $p1" } assertEquals("PLAYER: 1", callback("PLAYER", 1)) assertEquals("PLAYER: 2", format.encodeToV8Value(callback).v8Function.call(this, V8Array { push("PLAYER"); push(2) })) } - @Test fun `encode invokable`() = v8.blockingLock { + @Test fun `encode invokable`() = v8.evaluateInJSThreadBlocking(runtime) { val callback = Invokable { (p0, p1) -> "$p0: $p1" } assertEquals("PLAYER: 1", callback("PLAYER", 1)) assertEquals("PLAYER: 2", format.encodeToV8Value(callback).v8Function.call(this, V8Array { push("PLAYER"); push(2) })) } - @Test fun `encode kcallable`() = v8.blockingLock { + @Test fun `encode kcallable`() = v8.evaluateInJSThreadBlocking(runtime) { class Container { fun callback(p0: String, p1: Int) = "$p0: $p1" } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/JsonEncodingTests.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/JsonEncodingTests.kt index e77b4f71e..dc86a8cea 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/JsonEncodingTests.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/encoding/JsonEncodingTests.kt @@ -1,10 +1,10 @@ package com.intuit.player.jvm.j2v8.bridge.serialization.encoding import com.intuit.player.jvm.core.flow.FlowResult -import com.intuit.player.jvm.j2v8.base.AutoAcquireJ2V8Test +import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.j2v8.bridge.V8Node import com.intuit.player.jvm.j2v8.bridge.serialization.format.encodeToV8Value -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import com.intuit.player.jvm.j2v8.v8Object import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -14,7 +14,7 @@ import kotlinx.serialization.json.put import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -internal class JsonEncodingTests : AutoAcquireJ2V8Test() { +internal class JsonEncodingTests : J2V8Test() { private val expectedJson = buildJsonObject { put("data", buildJsonObject { put("a", "b") }) put( @@ -22,11 +22,11 @@ internal class JsonEncodingTests : AutoAcquireJ2V8Test() { buildJsonObject { put("state_type", "END") put("outcome", "doneWithTopic") - } + }, ) } private val expectedJsonString = expectedJson.toString() - private val expectedV8Object = v8.blockingLock { + private val expectedV8Object = v8.evaluateInJSThreadBlocking(runtime) { executeObjectScript("""($expectedJsonString)""") } private val expectedFlowResult = FlowResult(V8Node(expectedV8Object, runtime)) @@ -45,7 +45,9 @@ internal class JsonEncodingTests : AutoAcquireJ2V8Test() { @Test fun testToV8() { - expectedV8Object.assertEquivalent(format.encodeToV8Value(expectedFlowResult)) + expectedV8Object.evaluateInJSThreadBlocking(runtime) { + expectedV8Object.assertEquivalent(format.encodeToV8Value(expectedFlowResult)) + } } @Test @@ -56,10 +58,12 @@ internal class JsonEncodingTests : AutoAcquireJ2V8Test() { @Test fun testToAndFromV8() { - val v8Object = format.encodeToV8Value(expectedFlowResult).v8Object - expectedV8Object.assertEquivalent(v8Object) + expectedV8Object.evaluateInJSThreadBlocking(runtime) { + val v8Object = format.encodeToV8Value(expectedFlowResult).v8Object + expectedV8Object.assertEquivalent(v8Object) - val flow = format.decodeFromRuntimeValue(FlowResult.serializer(), v8Object) - Assertions.assertEquals(expectedFlowResult, flow) + val flow = format.decodeFromRuntimeValue(FlowResult.serializer(), v8Object) + Assertions.assertEquals(expectedFlowResult, flow) + } } } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8FormatTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8FormatTest.kt index 3910d78e5..c67f3d3dd 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8FormatTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/format/J2V8FormatTest.kt @@ -4,10 +4,11 @@ import com.eclipsesource.v8.V8 import com.eclipsesource.v8.V8Object import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.j2v8.V8Function import com.intuit.player.jvm.j2v8.base.J2V8Test -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking import kotlinx.serialization.Serializable import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer @@ -16,7 +17,7 @@ import org.junit.jupiter.api.Test internal class J2V8FormatTest : J2V8Test() { - @Test fun `encode simple map into V8Object with explicit serializers`() = format.v8.blockingLock { + @Test fun `encode simple map into V8Object with explicit serializers`() = format.v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf( "one" to 1, "two" to 2, @@ -29,7 +30,7 @@ internal class J2V8FormatTest : J2V8Test() { assertEquals(V8.getUndefined(), v8Object.get("three")) } - @Test fun `encode simple map into V8Object with implicit serializers`() = format.v8.blockingLock { + @Test fun `encode simple map into V8Object with implicit serializers`() = format.v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf( "one" to 1, "two" to 2, @@ -42,7 +43,7 @@ internal class J2V8FormatTest : J2V8Test() { assertEquals(V8.getUndefined(), v8Object.get("three")) } - @Test fun `encode nested map into V8Object with implicit serializers`() = format.v8.blockingLock { + @Test fun `encode nested map into V8Object with implicit serializers`() = format.v8.evaluateInJSThreadBlocking(runtime) { val map = mapOf( "one" to mapOf("three" to 3), "two" to mapOf("four" to 4), @@ -55,7 +56,7 @@ internal class J2V8FormatTest : J2V8Test() { assertEquals(V8.getUndefined(), v8Object.get("five")) } - @Test fun `encode serializable into V8Object with implicit serializers`() = format.v8.blockingLock { + @Test fun `encode serializable into V8Object with implicit serializers`() = format.v8.evaluateInJSThreadBlocking(runtime) { @Serializable data class Simple( val one: Int = 1, @@ -70,7 +71,7 @@ internal class J2V8FormatTest : J2V8Test() { assertEquals(V8.getUndefined(), v8Object.get("three")) } - @Test fun `decode V8Object into serializable with implicit serializers`() = format.v8.blockingLock { + @Test fun `decode V8Object into serializable with implicit serializers`() = format.v8.evaluateInJSThreadBlocking(runtime) { @Serializable data class Simple( val one: Int = 1, @@ -82,16 +83,16 @@ internal class J2V8FormatTest : J2V8Test() { V8Object(this).apply { add("one", 3) add("two", 4) - } + }, ) assertEquals(3, simple.one) assertEquals(4, simple.two) } - @Test fun `decode into Node backed serializable`() = format.v8.blockingLock { + @Test fun `decode into Node backed serializable`() = format.v8.evaluateInJSThreadBlocking(runtime) { data class Simple(override val node: Node) : NodeWrapper { - fun increment(value: Int) = node.getFunction("increment")!!(value) + fun increment(value: Int) = node.getInvokable("increment")!!(value) } val simple = format.decodeFromRuntimeValue( @@ -101,15 +102,15 @@ internal class J2V8FormatTest : J2V8Test() { "increment", V8Function(format) { args -> args.getInteger(0) + 1 - } + }, ) - } + }, ) assertEquals(1, simple.increment(0)) } - @Test fun `decode function into data class`() = format.v8.blockingLock { + @Test fun `decode function into data class`() = format.v8.evaluateInJSThreadBlocking(runtime) { @Serializable data class Data( val one: Int = 1, @@ -124,9 +125,9 @@ internal class J2V8FormatTest : J2V8Test() { "increment", V8Function(format) { args -> args.getInteger(0) + 1 - } + }, ) - } + }, ) assertEquals(3, simple.one) diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/serializers/ThrowableSerializerTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/serializers/ThrowableSerializerTest.kt index d89804550..d50c5b100 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/serializers/ThrowableSerializerTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/bridge/serialization/serializers/ThrowableSerializerTest.kt @@ -6,7 +6,8 @@ import com.intuit.player.jvm.core.bridge.serialization.serializers.ThrowableSeri import com.intuit.player.jvm.core.player.PlayerException import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.j2v8.bridge.serialization.format.decodeFromV8Value -import com.intuit.player.jvm.j2v8.extensions.blockingLock +import com.intuit.player.jvm.j2v8.extensions.evaluateInJSThreadBlocking +import com.intuit.player.jvm.utils.normalizeStackTraceElements import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -18,7 +19,7 @@ internal class ThrowableSerializerTest : J2V8Test() { @Test fun `JS Error is deserialized as PlayerException (using regex)`() { - val error = format.v8.blockingLock { + val error = format.v8.evaluateInJSThreadBlocking(runtime) { executeObjectScript("""(new Error("hello"))""") } val exception = format.decodeFromRuntimeValue(ThrowableSerializer(), error) @@ -30,7 +31,7 @@ internal class ThrowableSerializerTest : J2V8Test() { """com.intuit.player.jvm.core.bridge.JSErrorException: Error: hello at .(:1) """, - exception.stackTraceToString() + exception.stackTraceToString(), ) } @@ -45,7 +46,7 @@ internal class ThrowableSerializerTest : J2V8Test() { className, methodName, fileName, - lineNumber + lineNumber, ) val exception = PlayerException("world") @@ -55,7 +56,7 @@ internal class ThrowableSerializerTest : J2V8Test() { assertTrue(error is V8Object) error as V8Object - error.blockingLock { + error.evaluateInJSThreadBlocking(runtime) { assertEquals("world", error.get("message")) assertEquals(exception.stackTraceToString(), error.getString("stack")) @@ -64,7 +65,7 @@ internal class ThrowableSerializerTest : J2V8Test() { format.encodeToRuntimeValue( SerializableStackTraceElement.serializer(), serializableStackTraceElement, - ).jsEquals(error.getArray("stackTrace").getObject(0)) + ).jsEquals(error.getArray("stackTrace").getObject(0)), ) } } @@ -84,7 +85,7 @@ internal class ThrowableSerializerTest : J2V8Test() { assertTrue(exception is PlayerException) exception as PlayerException assertEquals("hello world", exception.message) - assertEquals(listOf(stackTraceElement), exception.stackTrace.toList()) + assertEquals(arrayOf(stackTraceElement).normalizeStackTraceElements(), exception.stackTrace.normalizeStackTraceElements()) exception.printStackTrace() } @@ -99,14 +100,14 @@ internal class ThrowableSerializerTest : J2V8Test() { className, methodName, fileName, - lineNumber + lineNumber, ) val exception = PlayerException( "hello", PlayerException("world").apply { stackTrace = arrayOf(stackTraceElement) - } + }, ).apply { stackTrace = arrayOf(stackTraceElement) } @@ -116,7 +117,7 @@ internal class ThrowableSerializerTest : J2V8Test() { assertTrue(error is V8Object) error as V8Object - error.blockingLock { + error.evaluateInJSThreadBlocking(runtime) { assertEquals("hello", error.get("message")) assertEquals(exception.stackTraceToString(), error.getString("stack")) @@ -125,12 +126,12 @@ internal class ThrowableSerializerTest : J2V8Test() { format.encodeToRuntimeValue( SerializableStackTraceElement.serializer(), serializableStackTraceElement, - ).jsEquals(error.getArray("stackTrace").getObject(0)) + ).jsEquals(error.getArray("stackTrace").getObject(0)), ) val cause = format.decodeFromV8Value(error.getObject("cause")) assertEquals("world", cause.message) - assertEquals(exception.cause!!.stackTrace.toList(), cause.stackTrace.toList()) + assertEquals(exception.cause!!.stackTrace.normalizeStackTraceElements(), cause.stackTrace.normalizeStackTraceElements()) } } } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/InvokeTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/InvokeTest.kt index a1b3959ac..85a8e0830 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/InvokeTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/InvokeTest.kt @@ -3,22 +3,26 @@ package com.intuit.player.jvm.j2v8.extensions import com.eclipsesource.v8.V8 import com.eclipsesource.v8.V8Function import com.intuit.player.jvm.j2v8.V8Function -import com.intuit.player.jvm.j2v8.base.AutoAcquireJ2V8Test +import com.intuit.player.jvm.j2v8.base.J2V8Test import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -internal class InvokeTest : AutoAcquireJ2V8Test() { +internal class InvokeTest : J2V8Test() { @Test fun `test V8Function vararg invoke`() { - val func = v8.executeObjectScript("""(function(a,b){return a+b})""") as V8Function - assertEquals(4, func.call(v8, format.args(2, 2))) - assertEquals(4, func.invoke(format, 2, 2)) + v8.evaluateInJSThreadBlocking(runtime) { + val func = v8.executeObjectScript("""(function(a,b){return a+b})""") as V8Function + assertEquals(4, func.call(v8, format.args(2, 2))) + assertEquals(4, func.invoke(format, 2, 2)) + } } @Test fun `test V8Function creation helper`() { - assertEquals(V8.getUndefined(), V8Function(format) { Unit }()) - assertEquals(4, V8Function(format) { 2 + 2 }()) + v8.evaluateInJSThreadBlocking(runtime) { + assertEquals(V8.getUndefined(), V8Function(format) { Unit }(this@InvokeTest.format)) + assertEquals(4, V8Function(format) { 2 + 2 }(this@InvokeTest.format)) + } } } diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/UnlockTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/UnlockTest.kt index 0c41ee593..041eb1813 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/UnlockTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/extensions/UnlockTest.kt @@ -1,6 +1,8 @@ package com.intuit.player.jvm.j2v8.extensions import com.intuit.player.jvm.j2v8.base.J2V8Test +import com.intuit.player.jvm.utils.test.runBlockingTest +import kotlinx.coroutines.withContext import org.junit.jupiter.api.Assertions.assertNotNull import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Assertions.assertThrows @@ -16,14 +18,18 @@ internal class UnlockTest : J2V8Test() { } @Test - fun `unlock when no thread has the lock`() { - assertNull(v8.locker.thread) - v8.unlock() + fun `unlock when no thread has the lock`() = runBlockingTest { + withContext(runtime.dispatcher) { + v8.unlock() + } assertNull(v8.locker.thread) } @Test - fun `unlock when current thread has the lock`() { + fun `unlock when current thread has the lock`() = runBlockingTest { + withContext(runtime.dispatcher) { + v8.unlock() + } v8.locker.acquire() assertNotNull(v8.locker.thread) v8.unlock() diff --git a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/promise/V8PromiseTest.kt b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/promise/V8PromiseTest.kt index 3cf1442dd..9160e8deb 100644 --- a/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/promise/V8PromiseTest.kt +++ b/jvm/j2v8/src/test/kotlin/com/intuit/player/jvm/j2v8/promise/V8PromiseTest.kt @@ -2,12 +2,12 @@ package com.intuit.player.jvm.j2v8.promise import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.Promise -import com.intuit.player.jvm.j2v8.base.AutoAcquireJ2V8Test +import com.intuit.player.jvm.j2v8.base.J2V8Test import com.intuit.player.jvm.utils.test.PromiseUtils import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -internal class V8PromiseTest : AutoAcquireJ2V8Test(), PromiseUtils { +internal class V8PromiseTest : J2V8Test(), PromiseUtils { override val thenChain = mutableListOf() override val catchChain = mutableListOf() @@ -21,7 +21,7 @@ internal class V8PromiseTest : AutoAcquireJ2V8Test(), PromiseUtils { const promise = new Promise(function(resolve, reject) { asdf.asdf.asdf.asdf }); return [promise, resolver]; })(); - """.trimIndent() + """.trimIndent(), ) as List<*> Promise(promise as Node).thenRecord.catchRecord @@ -39,7 +39,7 @@ internal class V8PromiseTest : AutoAcquireJ2V8Test(), PromiseUtils { at .(:3) at .(:5) """, - exception.stackTraceToString() + exception.stackTraceToString(), ) } } diff --git a/jvm/perf/src/main/kotlin/com/intuit/player/jvm/perf/jmh/PlayerPerf.kt b/jvm/perf/src/main/kotlin/com/intuit/player/jvm/perf/jmh/PlayerPerf.kt index 91501a951..153283102 100644 --- a/jvm/perf/src/main/kotlin/com/intuit/player/jvm/perf/jmh/PlayerPerf.kt +++ b/jvm/perf/src/main/kotlin/com/intuit/player/jvm/perf/jmh/PlayerPerf.kt @@ -13,7 +13,15 @@ import com.intuit.player.jvm.core.player.HeadlessPlayer import kotlinx.coroutines.runBlocking import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable -import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.annotations.Benchmark +import org.openjdk.jmh.annotations.BenchmarkMode +import org.openjdk.jmh.annotations.CompilerControl +import org.openjdk.jmh.annotations.Mode +import org.openjdk.jmh.annotations.OutputTimeUnit +import org.openjdk.jmh.annotations.Param +import org.openjdk.jmh.annotations.Scope +import org.openjdk.jmh.annotations.Setup +import org.openjdk.jmh.annotations.State import org.openjdk.jmh.infra.Blackhole import java.util.concurrent.TimeUnit import kotlin.coroutines.resume @@ -105,7 +113,7 @@ open class RuntimePlayerCreation : RuntimePerformance() { @CompilerControl(CompilerControl.Mode.DONT_INLINE) @Benchmark fun createHeadlessPlayer(consumer: Blackhole) { - consumer.consume(HeadlessPlayer(runtime = jsRuntime)) + consumer.consume(HeadlessPlayer(explicitRuntime = jsRuntime)) } @CompilerControl(CompilerControl.Mode.DONT_INLINE) @@ -149,7 +157,7 @@ open class HeadlessPlayerFlowPerformance : ContentPerformance() { @CompilerControl(CompilerControl.Mode.DONT_INLINE) @Benchmark fun startFlow(consumer: Blackhole) { - val player = HeadlessPlayer(runtime = jsRuntime) + val player = HeadlessPlayer(explicitRuntime = jsRuntime) consumer.consume(runBlocking { suspendCoroutine { player.hooks.viewController.tap("perf") { vc -> @@ -167,7 +175,7 @@ open class HeadlessPlayerFlowPerformance : ContentPerformance() { @CompilerControl(CompilerControl.Mode.DONT_INLINE) @Benchmark fun startAndUpdateFlow(consumer: Blackhole) { - val player = HeadlessPlayer(runtime = jsRuntime) + val player = HeadlessPlayer(explicitRuntime = jsRuntime) consumer.consume(runBlocking { suspendCoroutine { var dataController: DataController? = null diff --git a/jvm/perf/src/test/kotlin/com/intuit/player/jvm/perf/junit/RuntimePerfTest.kt b/jvm/perf/src/test/kotlin/com/intuit/player/jvm/perf/junit/RuntimePerfTest.kt index ad252ac5a..e062ce12e 100644 --- a/jvm/perf/src/test/kotlin/com/intuit/player/jvm/perf/junit/RuntimePerfTest.kt +++ b/jvm/perf/src/test/kotlin/com/intuit/player/jvm/perf/junit/RuntimePerfTest.kt @@ -1,11 +1,14 @@ package com.intuit.player.jvm.perf.junit import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.runtime.add import com.intuit.player.jvm.core.player.JSPlayerConfig import com.intuit.player.jvm.core.plugins.JSPluginWrapper import com.intuit.player.jvm.core.plugins.Plugin -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.TestTemplate internal class RuntimePerfTest : JSEngineTest() { @@ -30,7 +33,7 @@ internal class RuntimePerfTest : JSEngineTest() { val person = runtime.getObject("person") var resultString = "" captureTime { - resultString = runtime.getFunction("getAge")?.invoke(person) ?: "" + resultString = runtime.getInvokable("getAge")?.invoke(person) ?: "" } assert(resultString.isNotEmpty()) assertEquals("Joe is 25 years old", resultString) diff --git a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/testutils/Node.kt b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/testutils/Node.kt index 4ef834afa..b608d1917 100644 --- a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/testutils/Node.kt +++ b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/testutils/Node.kt @@ -3,12 +3,11 @@ package com.intuit.player.jvm.testutils import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.runtime.Runtime -import kotlinx.serialization.ContextualSerializer +import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.builtins.MapSerializer -import kotlinx.serialization.builtins.nullable import kotlinx.serialization.builtins.serializer -import kotlinx.serialization.json.* +import kotlinx.serialization.json.Json import kotlin.reflect.jvm.reflect /** @@ -19,7 +18,7 @@ import kotlin.reflect.jvm.reflect */ public class Node(private val map: Map) : com.intuit.player.jvm.core.bridge.Node, Map by map { - override fun getFunction(key: String): Invokable? = get(key)?.let { + override fun getInvokable(key: String, deserializationStrategy: DeserializationStrategy): Invokable? = get(key)?.let { when (it) { is Function<*> -> it else -> null @@ -31,13 +30,13 @@ public class Node(private val map: Map) : com.intuit.player.jvm.co } override fun getSerializable(key: String, deserializer: DeserializationStrategy): T = get(key)?.let { value -> - Json.decodeFromJsonElement(deserializer, Json.encodeToJsonElement(ContextualSerializer(Any::class).nullable, value)) + Json.decodeFromJsonElement(deserializer, Json.encodeToJsonElement(GenericSerializer(), value)) }!! override fun deserialize(deserializer: DeserializationStrategy): T = Json.decodeFromJsonElement( deserializer, - Json.encodeToJsonElement(MapSerializer(String.serializer(), ContextualSerializer(Any::class).nullable), map) + Json.encodeToJsonElement(MapSerializer(String.serializer(), GenericSerializer()), map), ) override fun isReleased(): Boolean = false diff --git a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PlayerTest.kt b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PlayerTest.kt index 1f9a05ad4..30ac6bf67 100644 --- a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PlayerTest.kt +++ b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PlayerTest.kt @@ -28,7 +28,7 @@ public abstract class PlayerTest : RuntimeTest(), Pluggable, LoggerPlugin by Tes /** Helper method for setting a [player] with configurable [plugins] and [runtime] */ public fun setupPlayer(plugins: List = this.plugins + this, runtime: Runtime<*> = this.runtime) { - player = HeadlessPlayer(plugins, runtime) + player = HeadlessPlayer(plugins, runtime, config = runtime.config) } } diff --git a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PromiseUtils.kt b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PromiseUtils.kt index 7c160ccdc..5fe6ff81f 100644 --- a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PromiseUtils.kt +++ b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/PromiseUtils.kt @@ -12,7 +12,7 @@ public interface PromiseUtils { public fun assertThen(vararg expected: Any?): Unit = assertChain( thenChain, - *expected + *expected, ) public fun assertCatch(vararg expected: Any?): Unit = assertChain( catchChain.map { @@ -21,7 +21,7 @@ public interface PromiseUtils { else -> it } }, - *expected + *expected, ) public fun assertChain(chain: List, vararg expected: Any?): Unit = Assertions.assertEquals(expected.asList(), chain) diff --git a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/runBlockingTest.kt b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/RunBlockingTest.kt similarity index 100% rename from jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/runBlockingTest.kt rename to jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/RunBlockingTest.kt diff --git a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/RuntimeTest.kt b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/RuntimeTest.kt index 92240cb4c..5405a2119 100644 --- a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/RuntimeTest.kt +++ b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/RuntimeTest.kt @@ -1,10 +1,18 @@ package com.intuit.player.jvm.utils.test -import com.intuit.player.jvm.core.bridge.runtime.* +import com.intuit.player.jvm.core.bridge.runtime.PlayerRuntimeContainer +import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.runtimeContainers +import com.intuit.player.jvm.core.bridge.runtime.runtimeFactory import com.intuit.player.jvm.core.bridge.serialization.format.RuntimeFormat import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestTemplate -import org.junit.jupiter.api.extension.* +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extension +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.TestTemplateInvocationContext +import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider /** * Simple base test class for testing each [Runtime] implementation on the classpath. Tests should be diff --git a/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/testMocks.kt b/jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/TestMocks.kt similarity index 100% rename from jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/testMocks.kt rename to jvm/testutils/src/main/kotlin/com/intuit/player/jvm/utils/test/TestMocks.kt diff --git a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/json.kt b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/Json.kt similarity index 82% rename from jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/json.kt rename to jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/Json.kt index 49f2eafc2..fef3d686b 100644 --- a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/json.kt +++ b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/Json.kt @@ -1,6 +1,12 @@ package com.intuit.player.jvm.utils -import kotlinx.serialization.json.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject internal val JsonElement.safeJsonObject: JsonObject? get() = try { jsonObject } catch (e: IllegalArgumentException) { null } internal val JsonElement.safeJsonArray: JsonArray? get() = try { jsonArray } catch (e: IllegalArgumentException) { null } diff --git a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/makeFlow.kt b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/MakeFlow.kt similarity index 90% rename from jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/makeFlow.kt rename to jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/MakeFlow.kt index e6d6b5e87..c4b1ee458 100644 --- a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/makeFlow.kt +++ b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/MakeFlow.kt @@ -1,6 +1,7 @@ package com.intuit.player.jvm.utils import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.runtime.Runtime import com.intuit.player.jvm.core.bridge.runtime.runtimeFactory import com.intuit.player.jvm.core.bridge.toJson @@ -18,7 +19,7 @@ public open class MakeFlowModule internal constructor() : JSScriptPluginWrapper( public fun makeFlow(flow: Node): JsonElement { ensureInitialized() - return instance.getFunction("makeFlow").let(::requireNotNull)(flow).toJson() + return instance.getInvokable("makeFlow").let(::requireNotNull)(flow).toJson() } public fun makeFlow(flow: String): JsonElement { diff --git a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/player.kt b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/Player.kt similarity index 100% rename from jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/player.kt rename to jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/Player.kt diff --git a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/StackTraceElements.kt b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/StackTraceElements.kt new file mode 100644 index 000000000..5510e596d --- /dev/null +++ b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/StackTraceElements.kt @@ -0,0 +1,15 @@ +package com.intuit.player.jvm.utils + +import com.intuit.player.jvm.core.bridge.serialization.serializers.ThrowableSerializer.SerializableStackTraceElement +import com.intuit.player.jvm.core.utils.InternalPlayerApi + +/** Make a collection of [StackTraceElement]s subject to element equality */ +@InternalPlayerApi +public fun Array.normalizeStackTraceElements(): List = map { stackTraceElement -> + SerializableStackTraceElement( + stackTraceElement.className, + stackTraceElement.methodName, + stackTraceElement.fileName, + stackTraceElement.lineNumber, + ) +} diff --git a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/mocks/ClassLoaderMock.kt b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/mocks/ClassLoaderMock.kt index 67a828e65..17d6c657a 100644 --- a/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/mocks/ClassLoaderMock.kt +++ b/jvm/utils/src/main/kotlin/com/intuit/player/jvm/utils/mocks/ClassLoaderMock.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.Serializable public data class ClassLoaderMock( override val group: String, override val name: String, - override val path: String + override val path: String, ) : Mock { /** Helper to provide default [ClassLoader] overload of [read] */ diff --git a/jvm/utils/src/test/kotlin/com/intuit/player/jvm/utils/MakeFlowTests.kt b/jvm/utils/src/test/kotlin/com/intuit/player/jvm/utils/MakeFlowTests.kt index 07f106a3c..19f592b4f 100644 --- a/jvm/utils/src/test/kotlin/com/intuit/player/jvm/utils/MakeFlowTests.kt +++ b/jvm/utils/src/test/kotlin/com/intuit/player/jvm/utils/MakeFlowTests.kt @@ -15,7 +15,7 @@ class MakeFlowTests { buildJsonObject { put("id", "some-id") put("type", "some-type") - } + }, ) assertTrue(flow is JsonObject) @@ -27,7 +27,7 @@ class MakeFlowTests { "data", "navigation", ), - keys + keys, ) } } diff --git a/plugins/beacon/jvm/src/main/kotlin/com/intuit/player/plugins/beacon/BeaconPlugin.kt b/plugins/beacon/jvm/src/main/kotlin/com/intuit/player/plugins/beacon/BeaconPlugin.kt index c08895584..411b3b4b3 100644 --- a/plugins/beacon/jvm/src/main/kotlin/com/intuit/player/plugins/beacon/BeaconPlugin.kt +++ b/plugins/beacon/jvm/src/main/kotlin/com/intuit/player/plugins/beacon/BeaconPlugin.kt @@ -1,7 +1,10 @@ package com.intuit.player.plugins.beacon import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.getInvokable +import com.intuit.player.jvm.core.bridge.runtime import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.runtime.add import com.intuit.player.jvm.core.bridge.serialization.serializers.GenericSerializer import com.intuit.player.jvm.core.player.Player @@ -10,6 +13,7 @@ import com.intuit.player.jvm.core.plugins.JSScriptPluginWrapper import com.intuit.player.jvm.core.plugins.Pluggable import com.intuit.player.jvm.core.plugins.findPlugin import com.intuit.player.plugins.settimeout.SetTimeoutPlugin +import kotlinx.coroutines.launch import kotlinx.serialization.Contextual import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -41,7 +45,7 @@ public class BeaconPlugin(override val plugins: List) : JSScrip } } - runtime.execute(script) + runtime.load(ScriptContext(if (runtime.config.debuggable) debugScript else script, bundledSourcePath)) runtime.add("beaconOptions", config) instance = runtime.buildInstance("(new $name.$name(beaconOptions))") } @@ -55,14 +59,16 @@ public class BeaconPlugin(override val plugins: List) : JSScrip /** Fire a beacon event */ public fun beacon(action: String, element: String, asset: Asset, data: Any? = null) { - instance.getFunction("beacon")!!.invoke( - mapOf( - "action" to action, - "element" to element, - "asset" to asset, - "data" to data, + runtime.scope.launch { + instance.getInvokable("beacon")!!.invoke( + mapOf( + "action" to action, + "element" to element, + "asset" to asset, + "data" to data, + ), ) - ) + } } private companion object { @@ -73,7 +79,7 @@ public class BeaconPlugin(override val plugins: List) : JSScrip @Serializable internal data class JSBeaconPluginConfig( val plugins: List, - val callback: (@Contextual Any?) -> Unit + val callback: (@Contextual Any?) -> Unit, ) } diff --git a/plugins/beacon/jvm/src/test/kotlin/com/intuit/player/plugins/beacon/BeaconPluginTest.kt b/plugins/beacon/jvm/src/test/kotlin/com/intuit/player/plugins/beacon/BeaconPluginTest.kt index d5addcfed..ca4ecf02a 100644 --- a/plugins/beacon/jvm/src/test/kotlin/com/intuit/player/plugins/beacon/BeaconPluginTest.kt +++ b/plugins/beacon/jvm/src/test/kotlin/com/intuit/player/plugins/beacon/BeaconPluginTest.kt @@ -8,10 +8,14 @@ import com.intuit.player.jvm.utils.test.RuntimePluginTest import com.intuit.player.jvm.utils.test.runBlockingTest import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking -import kotlinx.serialization.json.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import org.amshove.kluent.`should be instance of` -import org.amshove.kluent.`should not be null` import org.amshove.kluent.`should not be` +import org.amshove.kluent.`should not be null` import org.amshove.kluent.shouldBe import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.TestTemplate diff --git a/plugins/check-path/jvm/src/main/kotlin/CheckPathPlugin.kt b/plugins/check-path/jvm/src/main/kotlin/CheckPathPlugin.kt index 911cf0ca4..1ceac6507 100644 --- a/plugins/check-path/jvm/src/main/kotlin/CheckPathPlugin.kt +++ b/plugins/check-path/jvm/src/main/kotlin/CheckPathPlugin.kt @@ -1,6 +1,7 @@ package com.intuit.player.plugins.checkpath import com.intuit.player.jvm.core.asset.Asset +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.player.Player import com.intuit.player.jvm.core.plugins.JSScriptPluginWrapper import com.intuit.player.jvm.core.plugins.findPlugin @@ -9,7 +10,7 @@ import com.intuit.player.jvm.core.plugins.findPlugin public class CheckPathPlugin : JSScriptPluginWrapper(pluginName, sourcePath = bundledSourcePath) { /** Get the [Asset] represented by the [id] */ - public fun getAsset(id: String): Asset? = instance.getFunction("getAsset")?.invoke(id) as? Asset + public fun getAsset(id: String): Asset? = instance.getInvokable("getAsset")?.invoke(id) as? Asset private companion object { private const val bundledSourcePath = "plugins/check-path/core/dist/check-path-plugin.prod.js" diff --git a/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/FlowScopePlugin.kt b/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/FlowScopePlugin.kt index c23c6f671..ef85114ca 100644 --- a/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/FlowScopePlugin.kt +++ b/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/FlowScopePlugin.kt @@ -3,10 +3,15 @@ package com.intuit.player.plugins.coroutines import com.intuit.player.jvm.core.flow.Flow import com.intuit.player.jvm.core.player.Player import com.intuit.player.jvm.core.player.state.InProgressState +import com.intuit.player.jvm.core.player.subScope import com.intuit.player.jvm.core.plugins.PlayerPlugin import com.intuit.player.jvm.core.plugins.findPlugin -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import kotlinx.coroutines.job import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext /** Simple [PlayerPlugin] that provides a [CoroutineScope] reflective of the current [Flow] */ public class FlowScopePlugin : PlayerPlugin { @@ -20,14 +25,14 @@ public class FlowScopePlugin : PlayerPlugin { flowScope?.cancel("player changed state: $state") if (state is InProgressState) { // only create a new scope for new flows, determined on InProgressState updates - flowScope = CoroutineScope(Dispatchers.Default + FlowContext(state.flow) + SupervisorJob()) + flowScope = player.subScope(FlowContext(state.flow)) } } } /** Build a child scope of the current [flowScope] to ensure that these scopes will be structured according to the current [Flow] */ - public fun subScope(coroutineContext: CoroutineContext = Dispatchers.Default): CoroutineScope? = flowScope?.let { - CoroutineScope(it.coroutineContext[Job].let(::SupervisorJob) + coroutineContext) + public fun subScope(coroutineContext: CoroutineContext = EmptyCoroutineContext): CoroutineScope? = flowScope?.let { + CoroutineScope(it.coroutineContext + SupervisorJob(it.coroutineContext.job) + coroutineContext) } /** [CoroutineContext.Element] that contains the [Flow] bound to the current [CoroutineContext] */ @@ -52,5 +57,5 @@ public val Player.flowScopePlugin: FlowScopePlugin? get() = findPlugin() public val Player.flowScope: CoroutineScope? get() = flowScopePlugin?.flowScope /** Convenience method for building a subscope of the [flowScope] */ -public fun Player.subScope(coroutineContext: CoroutineContext = Dispatchers.Default): CoroutineScope? = +public fun Player.subScope(coroutineContext: CoroutineContext = EmptyCoroutineContext): CoroutineScope? = flowScopePlugin?.subScope(coroutineContext) diff --git a/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/UpdatesPlugin.kt b/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/UpdatesPlugin.kt index cd1015611..dd2ffc79d 100644 --- a/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/UpdatesPlugin.kt +++ b/plugins/coroutines/jvm/src/main/kotlin/com/intuit/player/plugins/coroutines/UpdatesPlugin.kt @@ -2,18 +2,18 @@ package com.intuit.player.plugins.coroutines import com.intuit.player.jvm.core.asset.Asset import com.intuit.player.jvm.core.player.Player -import com.intuit.player.jvm.core.player.state.ReleasedState import com.intuit.player.jvm.core.plugins.PlayerPlugin import com.intuit.player.jvm.core.plugins.findPlugin -import kotlinx.coroutines.* +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout /** [PlayerPlugin] that converts view updates to a [ReceiveChannel] and provides functionality for waiting for an update */ public class UpdatesPlugin : PlayerPlugin { - private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default) - private val _updates: Channel = Channel() public val updates: ReceiveChannel by ::_updates @@ -40,16 +40,12 @@ public class UpdatesPlugin : PlayerPlugin { player.hooks.viewController.tap { vc -> vc?.hooks?.view?.tap { v -> v?.hooks?.onUpdate?.tap { asset -> - scope.launch { + player.scope.launch { _updates.send(asset) } } } } - - player.hooks.state.tap { state -> - if (state is ReleasedState) scope.cancel() - } } } diff --git a/plugins/coroutines/jvm/src/test/kotlin/com/intuit/player/plugins/coroutines/FlowScopePluginTest.kt b/plugins/coroutines/jvm/src/test/kotlin/com/intuit/player/plugins/coroutines/FlowScopePluginTest.kt index 8ce344d04..646fd710a 100644 --- a/plugins/coroutines/jvm/src/test/kotlin/com/intuit/player/plugins/coroutines/FlowScopePluginTest.kt +++ b/plugins/coroutines/jvm/src/test/kotlin/com/intuit/player/plugins/coroutines/FlowScopePluginTest.kt @@ -5,13 +5,21 @@ import com.intuit.player.jvm.core.player.Player import com.intuit.player.jvm.core.player.state.CompletedState import com.intuit.player.jvm.core.player.state.InProgressState import com.intuit.player.jvm.core.player.state.PlayerFlowState +import com.intuit.player.jvm.core.player.state.ReleasedState import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.invoke import io.mockk.junit5.MockKExtension import io.mockk.slot +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel import kotlinx.coroutines.isActive -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -19,9 +27,14 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(MockKExtension::class) internal class FlowScopePluginTest { + private val scope = CoroutineScope(Dispatchers.Default) + @MockK lateinit var player: Player + @MockK lateinit var inProgressState: InProgressState + @MockK lateinit var completedState: CompletedState + @MockK lateinit var flow: Flow var stateTap = slot<(PlayerFlowState?) -> Unit>() @@ -31,6 +44,7 @@ internal class FlowScopePluginTest { @BeforeEach fun setup() { every { player.hooks.state.tap(any(), capture(stateTap)) } returns "some-id" every { inProgressState.flow } returns flow + every { player.scope } returns scope flowScopePlugin = FlowScopePlugin().apply { apply(player) @@ -39,6 +53,7 @@ internal class FlowScopePluginTest { private fun transitionToInProgress() = stateTap.invoke(inProgressState) private fun transitionToEnd() = stateTap.invoke(completedState) + private fun transitionToReleased() = stateTap.invoke(ReleasedState) @Test fun testNewScope() { assertNull(flowScopePlugin.flowScope) @@ -47,7 +62,7 @@ internal class FlowScopePluginTest { assertTrue(flowScopePlugin.flowScope!!.isActive) } - @Test fun testScopeCancelled() { + @Test fun testScopeCancelledOnEnd() { assertNull(flowScopePlugin.flowScope) transitionToInProgress() assertNotNull(flowScopePlugin.flowScope) @@ -56,6 +71,15 @@ internal class FlowScopePluginTest { assertFalse(flowScopePlugin.flowScope!!.isActive) } + @Test fun testScopeCancelledOnReleased() { + assertNull(flowScopePlugin.flowScope) + transitionToInProgress() + assertNotNull(flowScopePlugin.flowScope) + assertTrue(flowScopePlugin.flowScope!!.isActive) + transitionToReleased() + assertFalse(flowScopePlugin.flowScope!!.isActive) + } + @Test fun testSubScopeCancelled() { assertNull(flowScopePlugin.flowScope) transitionToInProgress() @@ -79,6 +103,15 @@ internal class FlowScopePluginTest { assertEquals(flow, flowScopePlugin.flowScope!!.flow) val subScope = flowScopePlugin.subScope() assertNotNull(subScope) - assertNull(subScope!!.flow) + assertEquals(flow, subScope!!.flow) + } + + @Test fun testRuntimeScopeCancelsFlowScope() { + assertNull(flowScopePlugin.flowScope) + transitionToInProgress() + assertNotNull(flowScopePlugin.flowScope) + assertTrue(flowScopePlugin.flowScope!!.isActive) + scope.cancel() + assertFalse(flowScopePlugin.flowScope!!.isActive) } } diff --git a/plugins/expression/jvm/src/main/kotlin/com/intuit/player/plugins/expression/ExpressionPlugin.kt b/plugins/expression/jvm/src/main/kotlin/com/intuit/player/plugins/expression/ExpressionPlugin.kt index 7fb4810d0..5af6caaa3 100644 --- a/plugins/expression/jvm/src/main/kotlin/com/intuit/player/plugins/expression/ExpressionPlugin.kt +++ b/plugins/expression/jvm/src/main/kotlin/com/intuit/player/plugins/expression/ExpressionPlugin.kt @@ -2,7 +2,9 @@ package com.intuit.player.plugins.expression import com.intuit.player.jvm.core.bridge.Invokable import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.runtime.add import com.intuit.player.jvm.core.plugins.JSScriptPluginWrapper @@ -16,26 +18,26 @@ public typealias ExpressionHandler = (List) -> Any? * Any subsequent expressions registered with the same name will override previous handlers. */ public class ExpressionPlugin( - public val map: Map + public val map: Map, ) : JSScriptPluginWrapper(pluginName, sourcePath = bundledSourcePath) { public constructor(vararg expressions: Pair) : this(expressions.toMap()) override fun apply(runtime: Runtime<*>) { - runtime.execute(script) + runtime.load(ScriptContext(if (runtime.config.debuggable) debugScript else script, bundledSourcePath)) runtime.add( "expressionHandlers", map.entries.fold(runtime.execute("new Map()") as Node) { acc, entry -> acc.apply { val (name, handler) = entry - getFunction("set")!!.invoke( + getInvokable("set")!!.invoke( name, Invokable { args -> handler.invoke(args.drop(1)) - } + }, ) } - } + }, ) instance = runtime.buildInstance("(new $name(expressionHandlers))") } diff --git a/plugins/expression/jvm/src/test/kotlin/com/intuit/player/plugins/expression/ExpressionPluginTest.kt b/plugins/expression/jvm/src/test/kotlin/com/intuit/player/plugins/expression/ExpressionPluginTest.kt index 77bcc92a3..c05a3a2e2 100644 --- a/plugins/expression/jvm/src/test/kotlin/com/intuit/player/plugins/expression/ExpressionPluginTest.kt +++ b/plugins/expression/jvm/src/test/kotlin/com/intuit/player/plugins/expression/ExpressionPluginTest.kt @@ -7,7 +7,10 @@ import com.intuit.player.jvm.core.plugins.findPlugin import com.intuit.player.jvm.utils.test.PlayerTest import com.intuit.player.jvm.utils.test.setupPlayer import com.intuit.player.jvm.utils.test.simpleFlowString -import org.amshove.kluent.* +import org.amshove.kluent.`should be empty` +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should be instance of` +import org.amshove.kluent.`should not be null` import org.junit.jupiter.api.TestTemplate internal class ExpressionPluginTest : PlayerTest() { @@ -21,8 +24,8 @@ internal class ExpressionPluginTest : PlayerTest() { else -> "bye" } }, - "MyExpression2" to { null } - ) + "MyExpression2" to { null }, + ), ) private val expressionPlugin get() = player.findPlugin()!! diff --git a/plugins/external-action/jvm/src/main/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPlugin.kt b/plugins/external-action/jvm/src/main/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPlugin.kt index 033fd3b6d..7b082dfa6 100644 --- a/plugins/external-action/jvm/src/main/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPlugin.kt +++ b/plugins/external-action/jvm/src/main/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPlugin.kt @@ -3,6 +3,7 @@ package com.intuit.player.plugins.externalAction import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.Promise import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.runtime.add import com.intuit.player.jvm.core.flow.state.NavigationFlowExternalState import com.intuit.player.jvm.core.flow.state.NavigationFlowState @@ -20,7 +21,7 @@ public fun interface ExternalActionHandler { /** Core plugin wrapper providing external action support for the JVM Player */ public class ExternalActionPlugin( - private var handler: ExternalActionHandler? = null + private var handler: ExternalActionHandler? = null, ) : JSScriptPluginWrapper(pluginName, sourcePath = bundledSourcePath), PlayerPlugin { private lateinit var player: Player @@ -31,11 +32,11 @@ public class ExternalActionPlugin( override fun apply(runtime: Runtime<*>) { SetTimeoutPlugin().apply(runtime) - runtime.execute(script) + runtime.load(ScriptContext(if (runtime.config.debuggable) debugScript else script, bundledSourcePath)) runtime.add("externalActionHandler") externalActionHandler@{ state: Node, options: Node -> val state = state.deserialize(NavigationFlowState.serializer()) as? NavigationFlowExternalState ?: return@externalActionHandler null - val options = options.deserialize(ControllerState.serializer())!! + val options = options.deserialize(ControllerState.serializer()) return@externalActionHandler runtime.Promise { resolve, _ -> handler?.onExternalState(state, options, resolve) diff --git a/plugins/external-action/jvm/src/test/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPluginTest.kt b/plugins/external-action/jvm/src/test/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPluginTest.kt index a34be5dea..8ed634e7d 100644 --- a/plugins/external-action/jvm/src/test/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPluginTest.kt +++ b/plugins/external-action/jvm/src/test/kotlin/com/intuit/player/plugins/externalAction/ExternalActionPluginTest.kt @@ -3,11 +3,9 @@ package com.intuit.player.plugins.externalAction import com.intuit.player.jvm.core.expressions.evaluate import com.intuit.player.jvm.utils.test.PlayerTest import com.intuit.player.jvm.utils.test.runBlockingTest -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking -import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.TestTemplate import org.junit.jupiter.api.assertThrows @@ -78,14 +76,14 @@ internal class ExternalActionPluginTest : PlayerTest() { runBlocking { player.start(jsonFlow).await() } - }.message + }.message, ) } @TestTemplate fun testExternalStateHandlingWithDelay() = runBlockingTest { player.onExternalAction { _, _, transition -> - GlobalScope.launch { + launch { delay(2000) transition("Next") } diff --git a/plugins/java-logger/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPlugin.kt b/plugins/java-logger/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPlugin.kt index 7086ed4b7..7ef43ad11 100644 --- a/plugins/java-logger/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPlugin.kt +++ b/plugins/java-logger/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPlugin.kt @@ -33,7 +33,7 @@ public class JavaLoggerPlugin(public val config: JavaLoggerConfig) : LoggerPlugi } public class JavaLoggerConfig( - public var name: String = "JavaLogger", + override var name: String = "JavaLogger", public var logger: (Logger) -> Unit = {}, ) : PlayerLoggingConfig() diff --git a/plugins/java-logger/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPluginTest.kt b/plugins/java-logger/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPluginTest.kt index f65a1a6cb..1cb29e3e3 100644 --- a/plugins/java-logger/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPluginTest.kt +++ b/plugins/java-logger/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/logging/jul/JavaLoggerPluginTest.kt @@ -18,7 +18,7 @@ internal class JavaLoggerPluginTest : RuntimeTest() { "MyLogger", JavaLoggerFactory.create { name = "MyLogger" - }.logger.name + }.logger.name, ) } diff --git a/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/metrics.kt b/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/Metrics.kt similarity index 69% rename from plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/metrics.kt rename to plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/Metrics.kt index fe0135ed1..25f2c69f6 100644 --- a/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/metrics.kt +++ b/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/Metrics.kt @@ -2,11 +2,14 @@ package com.intuit.player.jvm.plugins.metrics import com.intuit.player.jvm.core.bridge.Node import com.intuit.player.jvm.core.bridge.NodeWrapper +import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeSerializableField import com.intuit.player.jvm.core.bridge.serialization.serializers.NodeWrapperSerializer import com.intuit.player.jvm.core.bridge.serialization.serializers.PolymorphicNodeWrapperSerializer import com.intuit.player.jvm.core.player.PlayerException import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.nullable +import kotlinx.serialization.builtins.serializer internal class TimingSerializer : PolymorphicNodeWrapperSerializer() { override fun selectDeserializer(node: Node): KSerializer { @@ -21,7 +24,7 @@ internal class TimingSerializer : PolymorphicNodeWrapperSerializer() { @Serializable(with = TimingSerializer::class) public sealed class Timing(override val node: Node) : NodeWrapper { /** Time this duration started (ms) */ - public val startTime: Double? get() = node.getDouble("startTime") + public val startTime: Double? by NodeSerializableField(Double.serializer().nullable) } @Serializable(with = CompletedTiming.Serializer::class) @@ -29,10 +32,10 @@ public class CompletedTiming internal constructor(override val node: Node) : Tim public val completed: Boolean = true /** The time in (ms) that the process ended */ - public val endTime: Double? get() = node.getDouble("endTime") + public val endTime: Double? by NodeSerializableField(Double.serializer().nullable) /** The elapsed time of this event (ms) */ - public val duration: Int? get() = node.getInt("duration") + public val duration: Int? by NodeSerializableField(Int.serializer().nullable) internal object Serializer : NodeWrapperSerializer(::CompletedTiming) } @@ -46,35 +49,37 @@ public class IncompleteTiming internal constructor(override val node: Node) : Ti public sealed class NodeMetrics(override val node: Node) : Timing(node) { /** The type of the flow-state */ - public val stateType: String get() = node.getString("stateType") ?: "" + public val stateType: String by NodeSerializableField(String.serializer()) /** The name of the flow-state */ - public val stateName: String get() = node.getString("stateName") ?: "" + public val stateName: String by NodeSerializableField(String.serializer()) } @Serializable(with = RenderMetrics.Serializer::class) public class RenderMetrics(override val node: Node) : NodeMetrics(node) { /** Timing representing the initial render */ - public val render: Timing = node.getObject("render")?.deserialize(TimingSerializer())!! + public val render: Timing by NodeSerializableField(Timing.serializer()) internal object Serializer : NodeWrapperSerializer(::RenderMetrics) } +@Serializable(with = MetricsFlow.Serializer::class) public class MetricsFlow(override val node: Node) : NodeWrapper { /** The id of the flow these metrics are for */ - public val id: String? get() = node.getString("id") + public val id: String? by NodeSerializableField(String.serializer().nullable) /** request time */ - public val requestTime: Int? get() = node.getInt("requestTime") + public val requestTime: Int? by NodeSerializableField(Int.serializer().nullable) /** A timing measuring until the first interactive render */ - public val interactive: Timing get() = node.getObject("interactive")?.deserialize(TimingSerializer())!! + public val interactive: Timing by NodeSerializableField(Timing.serializer()) + + internal object Serializer : NodeWrapperSerializer(::MetricsFlow) } @Serializable(with = PlayerFlowMetrics.Serializer::class) public class PlayerFlowMetrics(override val node: Node) : Timing(node) { /** All metrics about a running flow */ - public val flow: MetricsFlow get() = MetricsFlow(node.getObject("flow")!!) - + public val flow: MetricsFlow by NodeSerializableField(MetricsFlow.serializer()) internal object Serializer : NodeWrapperSerializer(::PlayerFlowMetrics) } diff --git a/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPlugin.kt b/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPlugin.kt index b55a8a326..6056d0a03 100644 --- a/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPlugin.kt +++ b/plugins/metrics/jvm/src/main/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPlugin.kt @@ -1,7 +1,9 @@ package com.intuit.player.jvm.plugins.metrics import com.intuit.player.jvm.core.bridge.Node +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.runtime.add import com.intuit.player.jvm.core.player.Player import com.intuit.player.jvm.core.plugins.JSScriptPluginWrapper @@ -11,11 +13,11 @@ import com.intuit.player.jvm.core.plugins.findPlugin public typealias RenderEndHandler = (Timing?, RenderMetrics?, PlayerFlowMetrics?) -> Unit public class MetricsPlugin( - private val handler: RenderEndHandler + private val handler: RenderEndHandler, ) : JSScriptPluginWrapper(pluginName, sourcePath = bundledSourcePath) { override fun apply(runtime: Runtime<*>) { - runtime.execute(script) + runtime.load(ScriptContext(if (runtime.config.debuggable) debugScript else script, bundledSourcePath)) runtime.add( "handlers", mapOf( @@ -23,16 +25,16 @@ public class MetricsPlugin( handler.invoke( timing.deserialize(Timing.serializer()), renderMetrics.deserialize(RenderMetrics.serializer()), - flowMetrics.deserialize(PlayerFlowMetrics.serializer()) + flowMetrics.deserialize(PlayerFlowMetrics.serializer()), ) }, - "trackRenderTime" to true - ) + "trackRenderTime" to true, + ), ) instance = runtime.buildInstance("(new $name(handlers))") } - public fun renderEnd(): Unit = instance.getFunction("renderEnd")!!() + public fun renderEnd(): Unit = instance.getInvokable("renderEnd")!!() private companion object { private const val bundledSourcePath = "plugins/metrics/core/dist/metrics-plugin.prod.js" @@ -47,7 +49,7 @@ public typealias RequestTimeClosure = () -> Int /** Wrapper around RequestTimeWebPlugin, which needs to apply to MetricsPlugin */ internal class RequestTimeWebPlugin( - private val getRequestTime: RequestTimeClosure + private val getRequestTime: RequestTimeClosure, ) : JSScriptPluginWrapper(pluginName, sourcePath = bundledSourcePath) { override fun apply(runtime: Runtime<*>) { @@ -55,14 +57,14 @@ internal class RequestTimeWebPlugin( runtime.execute(script) } runtime.add( - "callback" + "callback", ) getRequestTime@{ getRequestTime.invoke() } instance = runtime.buildInstance("(new $name(callback))") } public fun apply(metricsPlugin: MetricsPlugin) { apply(metricsPlugin.instance.runtime) - instance.getFunction("apply")?.invoke(metricsPlugin.instance) + instance.getInvokable("apply")?.invoke(metricsPlugin.instance) } private companion object { @@ -73,7 +75,7 @@ internal class RequestTimeWebPlugin( /** A plugin to supply request time to MetricsPlugin */ public class RequestTimePlugin( - private val getRequestTime: RequestTimeClosure + private val getRequestTime: RequestTimeClosure, ) : PlayerPlugin { private val requestTimeWebPlugin = RequestTimeWebPlugin(getRequestTime) override fun apply(player: Player) { diff --git a/plugins/metrics/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPluginTest.kt b/plugins/metrics/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPluginTest.kt index f7fcb5590..0b2f9cf22 100644 --- a/plugins/metrics/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPluginTest.kt +++ b/plugins/metrics/jvm/src/test/kotlin/com/intuit/player/jvm/plugins/metrics/MetricsPluginTest.kt @@ -60,7 +60,7 @@ internal class RequestTimePluginTest : PlayerTest() { mockkObject(renderEndHandler) return listOf( MetricsPlugin(renderEndHandler), - RequestTimePlugin(getRequestTime) + RequestTimePlugin(getRequestTime), ) } diff --git a/plugins/pubsub/jvm/src/main/kotlin/com/intuit/player/plugins/pubsub/PubSubPlugin.kt b/plugins/pubsub/jvm/src/main/kotlin/com/intuit/player/plugins/pubsub/PubSubPlugin.kt index 56cd11530..824cfac90 100644 --- a/plugins/pubsub/jvm/src/main/kotlin/com/intuit/player/plugins/pubsub/PubSubPlugin.kt +++ b/plugins/pubsub/jvm/src/main/kotlin/com/intuit/player/plugins/pubsub/PubSubPlugin.kt @@ -1,6 +1,8 @@ package com.intuit.player.plugins.pubsub +import com.intuit.player.jvm.core.bridge.getInvokable import com.intuit.player.jvm.core.bridge.runtime.Runtime +import com.intuit.player.jvm.core.bridge.runtime.ScriptContext import com.intuit.player.jvm.core.bridge.runtime.add import com.intuit.player.jvm.core.player.Player import com.intuit.player.jvm.core.plugins.JSScriptPluginWrapper @@ -12,7 +14,7 @@ public class PubSubPlugin(public val config: Config? = null) : JSScriptPluginWra override fun apply(runtime: Runtime<*>) { config?.let { - runtime.execute(script) + runtime.load(ScriptContext(if (runtime.config.debuggable) debugScript else script, bundledSourcePath)) runtime.add("pubsubConfig", config) instance = runtime.buildInstance("(new $name(pubsubConfig))") } ?: super.apply(runtime) @@ -25,14 +27,14 @@ public class PubSubPlugin(public val config: Config? = null) : JSScriptPluginWra * @return subscription token used to [unsubscribe] */ public fun subscribe(eventName: String, block: (String, Any?) -> Unit): String = instance - .getFunction("subscribe")!!(eventName, block) + .getInvokable("subscribe")!!(eventName, block) /** * Cancel subscription registered with [token] * @param token subscription token obtained from [subscribe] call */ public fun unsubscribe(token: String) { - instance.getFunction("unsubscribe")!!(token) + instance.getInvokable("unsubscribe")!!(token) } /** @@ -41,12 +43,12 @@ public class PubSubPlugin(public val config: Config? = null) : JSScriptPluginWra * @param eventData Arbitrary data associated with the event */ public fun publish(eventName: String, eventData: Any) { - instance.getFunction("publish")!!(eventName, eventData) + instance.getInvokable("publish")!!(eventName, eventData) } @Serializable public data class Config( - public val expressionName: String + public val expressionName: String, ) private companion object { diff --git a/plugins/pubsub/jvm/src/test/kotlin/com/intuit/player/plugins/pubsub/PubSubPluginTest.kt b/plugins/pubsub/jvm/src/test/kotlin/com/intuit/player/plugins/pubsub/PubSubPluginTest.kt index 628930fb6..6d8050a79 100644 --- a/plugins/pubsub/jvm/src/test/kotlin/com/intuit/player/plugins/pubsub/PubSubPluginTest.kt +++ b/plugins/pubsub/jvm/src/test/kotlin/com/intuit/player/plugins/pubsub/PubSubPluginTest.kt @@ -76,7 +76,7 @@ internal class PubSubPluginTest : PlayerTest() { @TestTemplate fun pubsubWithMap() { val (expectedName, expectedData) = "eventName" to mapOf( - "some" to "data" + "some" to "data", ) var name: String? = null var data: Any? = null @@ -90,14 +90,14 @@ internal class PubSubPluginTest : PlayerTest() { @TestTemplate fun pubsubWithV8Object() { val (expectedName, expectedData) = "eventName" to mapOf( - "some" to "data" + "some" to "data", ) var name: String? = null var data: Any? = null plugin.subscribe(expectedName) { n, d -> name = n; data = d; println("EVENT: $n: ${Json.encodeToString(GenericSerializer(), d)}") } plugin.publish( expectedName, - expectedData + expectedData, ) name `should be equal to` expectedName @@ -107,7 +107,7 @@ internal class PubSubPluginTest : PlayerTest() { @TestTemplate fun pubsubWithDefaultName() { val (expectedName, expectedData) = "eventName" to mapOf( - "some" to "data" + "some" to "data", ) var name: String? = null var data: Any? = null @@ -123,7 +123,7 @@ internal class PubSubPluginTest : PlayerTest() { fun pubsubWithCustomName() { setupPlayer(PubSubPlugin(PubSubPlugin.Config("publishEvent"))) val (expectedName, expectedData) = "eventName" to mapOf( - "some" to "data" + "some" to "data", ) var name: String? = null var data: Any? = null diff --git a/plugins/reference-assets/android/src/androidTest/java/com/intuit/player/android/reference/assets/action/ActionTest.kt b/plugins/reference-assets/android/src/androidTest/java/com/intuit/player/android/reference/assets/action/ActionTest.kt index bc1245eef..2e7289f65 100644 --- a/plugins/reference-assets/android/src/androidTest/java/com/intuit/player/android/reference/assets/action/ActionTest.kt +++ b/plugins/reference-assets/android/src/androidTest/java/com/intuit/player/android/reference/assets/action/ActionTest.kt @@ -32,6 +32,7 @@ class ActionTest : AssetTest("reference-assets") { repeat(10) { assertEquals("Count: $it", text.toString()) performClick() + blockUntilRendered() } } @@ -50,6 +51,7 @@ class ActionTest : AssetTest("reference-assets") { collectionValues[0].shouldBeView