From dceac45ce831c321bfceada796a51374ed1cf019 Mon Sep 17 00:00:00 2001 From: Marcin Benke Date: Thu, 2 Jul 2026 09:04:33 +0200 Subject: [PATCH 01/18] WIP: browser IDE + in-memory compile API Snapshot of the GHCJS web IDE work before starting the typecheck-cache investigation. Includes: - Solcore.Api: in-memory compileSolcore entry point (source in, hull/yul out) - Frontend.Module.Loader: SourceFS abstraction + loadModuleGraphFromSource (virtual in-memory filesystem, embedded std bundle) - Std.{Bundle,Embed}, Util.Keccak: TH-embedded std sources, pure Keccak-256 - yule -> library module reorg (Language.Hull.*, Language.Yul.Builtins) - web/: React IDE (index.html/ide.jsx), simple.html, GHCJS FFI (Main.hs), worker.js, build.sh, vendored react Assisted-By: Claude Opus 4.8 --- .gitignore | 7 + cabal-ghcjs-o1.project | 8 + cabal-ghcjs.project | 10 + sol-core.cabal | 21 +- {yule => src/Language/Hull}/Compress.hs | 2 +- src/Language/Hull/ToYul/Assemble.hs | 102 +++++++ {yule => src/Language/Hull/ToYul}/Locus.hs | 2 +- {yule => src/Language/Hull/ToYul}/Options.hs | 2 +- {yule => src/Language/Hull/ToYul}/TM.hs | 10 +- .../Language/Hull/ToYul}/Translate.hs | 10 +- {yule => src/Language/Yul}/Builtins.hs | 2 +- src/Solcore/Api.hs | 71 +++++ src/Solcore/Backend/MastEval.hs | 10 +- src/Solcore/Frontend/Module/Loader.hs | 98 +++++-- src/Solcore/Pipeline/SolcorePipeline.hs | 19 +- src/Solcore/Std/Bundle.hs | 24 ++ src/Solcore/Std/Embed.hs | 19 ++ src/Solcore/Util/Keccak.hs | 122 ++++++++ test/InMemoryApiTests.hs | 48 ++++ test/KeccakTests.hs | 29 ++ test/Main.hs | 6 +- web/Main.hs | 58 ++++ web/build.sh | 76 +++++ web/ide.jsx | 206 ++++++++++++++ web/index.html | 55 ++++ web/simple.html | 114 ++++++++ web/solcore-web.cabal | 12 + web/vendor/react-dom.production.min.js | 267 ++++++++++++++++++ web/vendor/react.production.min.js | 31 ++ web/worker.js | 20 ++ yule/Main.hs | 54 +--- 31 files changed, 1420 insertions(+), 95 deletions(-) create mode 100644 cabal-ghcjs-o1.project create mode 100644 cabal-ghcjs.project rename {yule => src/Language/Hull}/Compress.hs (98%) create mode 100644 src/Language/Hull/ToYul/Assemble.hs rename {yule => src/Language/Hull/ToYul}/Locus.hs (95%) rename {yule => src/Language/Hull/ToYul}/Options.hs (97%) rename {yule => src/Language/Hull/ToYul}/TM.hs (93%) rename {yule => src/Language/Hull/ToYul}/Translate.hs (98%) rename {yule => src/Language/Yul}/Builtins.hs (86%) create mode 100644 src/Solcore/Api.hs create mode 100644 src/Solcore/Std/Bundle.hs create mode 100644 src/Solcore/Std/Embed.hs create mode 100644 src/Solcore/Util/Keccak.hs create mode 100644 test/InMemoryApiTests.hs create mode 100644 test/KeccakTests.hs create mode 100644 web/Main.hs create mode 100755 web/build.sh create mode 100644 web/ide.jsx create mode 100644 web/index.html create mode 100644 web/simple.html create mode 100644 web/solcore-web.cabal create mode 100644 web/vendor/react-dom.production.min.js create mode 100644 web/vendor/react.production.min.js create mode 100644 web/worker.js diff --git a/.gitignore b/.gitignore index c3b14e9af..96abdf505 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,10 @@ opencode.jsonc # config dirs .vscode/ .private-journal/ + +# GHCJS / browser build artifacts +dist-ghcjs/ +web/site/ + +# editor backups +*~ diff --git a/cabal-ghcjs-o1.project b/cabal-ghcjs-o1.project new file mode 100644 index 000000000..b851a5f04 --- /dev/null +++ b/cabal-ghcjs-o1.project @@ -0,0 +1,8 @@ +-- Release build for the GHC JavaScript backend: same as cabal-ghcjs.project +-- but optimised. Use with --builddir=dist-ghcjs-o1. +packages: . web + +with-compiler: javascript-unknown-ghcjs-ghc +with-hc-pkg: javascript-unknown-ghcjs-ghc-pkg + +optimization: 1 diff --git a/cabal-ghcjs.project b/cabal-ghcjs.project new file mode 100644 index 000000000..c9e985953 --- /dev/null +++ b/cabal-ghcjs.project @@ -0,0 +1,10 @@ +-- Build configuration for the GHC JavaScript backend (browser target). +-- Use with: cabal build --project-file=cabal-ghcjs.project --builddir=dist-ghcjs +packages: . web + +with-compiler: javascript-unknown-ghcjs-ghc +with-hc-pkg: javascript-unknown-ghcjs-ghc-pkg + +-- Faster iteration for the browser build; the compiler output is correctness +-- driven, not perf sensitive here. +optimization: 0 diff --git a/sol-core.cabal b/sol-core.cabal index 384034e87..15dd3725e 100644 --- a/sol-core.cabal +++ b/sol-core.cabal @@ -21,8 +21,6 @@ common common-opts , mtl , bytestring , containers - , cryptonite - , memory , algebraic-graphs , array , directory @@ -58,6 +56,7 @@ library -- cabal-fmt: expand src exposed-modules: + Solcore.Api Solcore.Backend.ComptimeCheck Solcore.Backend.EmitHull Solcore.Backend.Mast @@ -111,13 +110,25 @@ library Solcore.Pipeline.Options Solcore.Pipeline.SolcorePipeline Solcore.Primitives.Primitives + Solcore.Std.Bundle + Solcore.Std.Embed + Solcore.Util.Keccak Language.Hull + Language.Hull.Compress Language.Hull.Parser Language.Hull.TcEnv Language.Hull.TcMonad Language.Hull.TypeCheck Language.Hull.Types + -- Hull -> Yul backend (was the separate `yule` executable); promoted to + -- the library so the translation is reusable (CLI, tests, other tools). + Language.Hull.ToYul.Assemble + Language.Hull.ToYul.Locus + Language.Hull.ToYul.Options + Language.Hull.ToYul.TM + Language.Hull.ToYul.Translate Language.Yul + Language.Yul.Builtins Language.Yul.Parser Language.Yul.QuasiQuote Common.LightYear @@ -147,8 +158,8 @@ executable yule PatternSynonyms BlockArguments ImportQualifiedPost - other-modules: Locus, Options, TM, Translate, Builtins, Compress - build-depends: base ^>=4.19.1.0, + -- Locus, Options, TM, Translate, Builtins, Compress now live in the library. + build-depends: base >= 4.19.1.0, pretty >= 1.1, containers >= 0.6, mtl >= 2.3, @@ -174,6 +185,8 @@ test-suite sol-core-tests Cases ContractAbiTests HullCases + InMemoryApiTests + KeccakTests MatchCompilerTests ModuleTypeCheckTests SpecialiseTests diff --git a/yule/Compress.hs b/src/Language/Hull/Compress.hs similarity index 98% rename from yule/Compress.hs rename to src/Language/Hull/Compress.hs index db39642f1..1f1777926 100644 --- a/yule/Compress.hs +++ b/src/Language/Hull/Compress.hs @@ -1,4 +1,4 @@ -module Compress where +module Language.Hull.Compress where import Language.Hull diff --git a/src/Language/Hull/ToYul/Assemble.hs b/src/Language/Hull/ToYul/Assemble.hs new file mode 100644 index 000000000..0b160b449 --- /dev/null +++ b/src/Language/Hull/ToYul/Assemble.hs @@ -0,0 +1,102 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} + +-- | Assemble a translated Yul object into final Yul text, and drive the whole +-- hull -> Yul step in-process. This holds the wrapping logic that used to live +-- in @yule/Main.hs@, so both the @yule@ CLI and the in-memory API share it. +module Language.Hull.ToYul.Assemble + ( defaultYuleOptions, + objectToYul, + wrapInObject, + ) +where + +import Common.Pretty +import Control.Exception (SomeException, evaluate, try) +import Language.Hull (Object) +import Language.Hull.TcEnv (emptyHullTcEnv) +import Language.Hull.TcMonad (runHullTcM) +import Language.Hull.TypeCheck (checkObject) +import Language.Yul +import Language.Yul.QuasiQuote +import Language.Hull.ToYul.Options (Options (..)) +import Language.Hull.ToYul.TM (runTM) +import Language.Hull.ToYul.Translate (translateObject) + +-- | Options for driving the Yul backend in-process, matching the @yule@ CLI +-- defaults (deployment code on, type checking on). +defaultYuleOptions :: Options +defaultYuleOptions = + Options + { input = "", + contract = "Output", + output = "Output.sol", + verbose = False, + debug = False, + compress = False, + wrap = False, + runOnce = False, + noTypeCheck = False + } + +-- | Type-check, translate and render one hull object to Yul (with deployment +-- code). Returns @Left@ with a diagnostic on a Hull/Yul type error or an +-- unimplemented translation case. +objectToYul :: Object -> IO (Either String String) +objectToYul obj = do + tcResult <- runHullTcM (checkObject obj) emptyHullTcEnv + case tcResult of + Left err -> pure (Left err) + Right () -> do + -- The translator uses `error` for unimplemented cases; force the rendered + -- output inside `try` so those surface as diagnostics rather than crashes. + outcome <- + try $ do + yulPreobject <- runTM defaultYuleOptions (translateObject obj) + let rendered = render (wrapInObject True yulPreobject) + _ <- evaluate (length rendered) + pure rendered + pure $ case (outcome :: Either SomeException String) of + Left ex -> Left ("Yul translation error:\n" ++ show ex) + Right rendered -> Right rendered + +-- wrap in a Yul object with the given name +wrapInObject :: Bool -> YulObject -> Doc +wrapInObject deploy yulo@(YulObject name code inners) + | deploy = ppr (createDeployment yulo) + | otherwise = ppr (YulObject name (addMemInit (addRetCode code)) inners) + +addMemInit :: YulCode -> YulCode +addMemInit c = YulCode [[yulStmt| mstore(64, memoryguard(128)) |]] <> c + +addRetCode :: YulCode -> YulCode +addRetCode c = c <> retCode + where + retCode = + YulCode + [yulBlock| + { + mstore(0, _mainresult) + return(0, 32) + } + |] + +deployCode :: String -> Bool -> YulCode +deployCode _name withStart = YulCode $ go withStart + where + go True = [[yulStmt| usr$_start() |]] + go False = [] + +createDeployment :: YulObject -> YulObject +createDeployment (YulObject yulName yulCode [InnerObject (YulObject innerName innerCode [])]) = + YulObject yulName yulCode' [yulInner'] + where + yulCode' = yulCode <> deployCode innerName True + yulInner' = InnerObject (YulObject innerName (addRetCode innerCode) []) +createDeployment (YulObject yulName yulCode []) = + YulObject yulName' yulCode' [yulInner'] + where + yulName' = yulName <> "Deploy" + yulCode' = deployCode yulName False + yulInner' = InnerObject (YulObject yulName (addRetCode yulCode) []) +createDeployment _ = error ("createDeployment not implemented for this type of object") diff --git a/yule/Locus.hs b/src/Language/Hull/ToYul/Locus.hs similarity index 95% rename from yule/Locus.hs rename to src/Language/Hull/ToYul/Locus.hs index 8a2edd8ce..c22388883 100644 --- a/yule/Locus.hs +++ b/src/Language/Hull/ToYul/Locus.hs @@ -1,4 +1,4 @@ -module Locus where +module Language.Hull.ToYul.Locus where import Data.String diff --git a/yule/Options.hs b/src/Language/Hull/ToYul/Options.hs similarity index 97% rename from yule/Options.hs rename to src/Language/Hull/ToYul/Options.hs index 92e925732..e6fd605b6 100644 --- a/yule/Options.hs +++ b/src/Language/Hull/ToYul/Options.hs @@ -1,4 +1,4 @@ -module Options where +module Language.Hull.ToYul.Options where import Options.Applicative diff --git a/yule/TM.hs b/src/Language/Hull/ToYul/TM.hs similarity index 93% rename from yule/TM.hs rename to src/Language/Hull/ToYul/TM.hs index 0e61487ac..113c83754 100644 --- a/yule/TM.hs +++ b/src/Language/Hull/ToYul/TM.hs @@ -1,9 +1,9 @@ -module TM +module Language.Hull.ToYul.TM ( TM, runTM, CEnv (..), -- , module RIO - module Locus, + module Language.Hull.ToYul.Locus, FunInfo (..), getCounter, setCounter, @@ -25,9 +25,9 @@ import Control.Monad (when) import Data.Map (Map) import Data.Map qualified as Map import Language.Hull qualified as Hull -import Locus -import Options (Options) -import Options qualified +import Language.Hull.ToYul.Locus +import Language.Hull.ToYul.Options (Options) +import Language.Hull.ToYul.Options qualified as Options type VarEnv = Map String Location diff --git a/yule/Translate.hs b/src/Language/Hull/ToYul/Translate.hs similarity index 98% rename from yule/Translate.hs rename to src/Language/Hull/ToYul/Translate.hs index 3a8d92f02..a606ac926 100644 --- a/yule/Translate.hs +++ b/src/Language/Hull/ToYul/Translate.hs @@ -1,8 +1,8 @@ {-# LANGUAGE OverloadedStrings #-} -module Translate where +module Language.Hull.ToYul.Translate where -import Builtins +import Language.Yul.Builtins import Data.List (partition) import Data.Map qualified as Map import Data.String @@ -11,7 +11,7 @@ import Language.Hull hiding (Name) import Language.Hull qualified as Hull import Language.Yul import Solcore.Frontend.Syntax.Name -import TM +import Language.Hull.ToYul.TM genExpr :: Expr -> TM ([YulStmt], Location) genExpr (EWord n) = pure ([], LocWord n) @@ -242,6 +242,10 @@ genStmt (SExpr e) = fst <$> genExpr e genStmt SBreak = pure [YBreak] genStmt SContinue = pure [YContinue] genStmt (SRevert s) = pure (revertStmt s) +-- Comments carry no runtime meaning. The hull parser skips block comments, so +-- the standalone yule binary never sees these; drop them here too so the +-- in-memory backend behaves identically. +genStmt (SComment _) = pure [] genStmt e = error $ "genStmt unimplemented for: " ++ show e -- If the statement is a function definition, record its type diff --git a/yule/Builtins.hs b/src/Language/Yul/Builtins.hs similarity index 86% rename from yule/Builtins.hs rename to src/Language/Yul/Builtins.hs index afdee07ee..0c9ace959 100644 --- a/yule/Builtins.hs +++ b/src/Language/Yul/Builtins.hs @@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} -module Builtins (yulBuiltins, revertStmt) where +module Language.Yul.Builtins (yulBuiltins, revertStmt) where import Language.Yul diff --git a/src/Solcore/Api.hs b/src/Solcore/Api.hs new file mode 100644 index 000000000..1b4ab6989 --- /dev/null +++ b/src/Solcore/Api.hs @@ -0,0 +1,71 @@ +-- | In-memory compilation entry point. +-- +-- This is the seam the web IDE talks to: source text in (from an editor +-- buffer), structured result out (compiler output or diagnostics), with no +-- file-system access. It reuses the whole existing pipeline via +-- 'compileGraph', differing from the CLI only in how the module graph is +-- obtained. +module Solcore.Api + ( CompileResult (..), + compileSolcore, + defaultOptions, + ) +where + +import Control.Monad.Except (runExceptT) +import Data.List (intercalate) +import Language.Hull qualified as Hull +import Language.Hull.ToYul.Assemble (objectToYul) +import Solcore.Frontend.Module.Loader (loadModuleGraphFromSource) +import Solcore.Frontend.Pretty.SolcorePretty (pretty) +import Solcore.Pipeline.Options (Option, emptyOption) +import Solcore.Pipeline.SolcorePipeline (compileGraph) + +-- | Outcome of an in-memory compilation. +-- +-- * 'compileOutput' — pretty-printed Hull objects (the frontend result). +-- * 'compileYul' — generated Yul (the backend result), when the frontend +-- succeeded and Yul translation didn't fail. +-- * 'compileErrors' — diagnostics from whichever stage failed. +data CompileResult + = CompileResult + { compileOutput :: Maybe String, + compileYul :: Maybe String, + compileErrors :: [String] + } + deriving (Eq, Show) + +-- | Baseline options for the in-memory path. The file-system fields of +-- 'Option' (roots, output dir) are unused here; the UI is expected to override +-- the boolean pass toggles (checkboxes) and the partial-evaluation fuel (an +-- input field) on top of this. +defaultOptions :: Option +defaultOptions = emptyOption "Main.solc" + +-- | Compile a single solcore source module in memory. 'Option' is supplied by +-- the caller (the UI), so pass toggles and fuel are driven from the frontend. +compileSolcore :: Option -> String -> IO CompileResult +compileSolcore opts source = do + graphResult <- loadModuleGraphFromSource source + case graphResult of + Left err -> pure (CompileResult Nothing Nothing [err]) + Right graph -> do + compiled <- runExceptT (compileGraph opts graph) + case compiled of + Left err -> pure (CompileResult Nothing Nothing [err]) + Right objs -> do + let hull = renderObjects objs + yulResult <- objectsToYul objs + pure $ case yulResult of + Left err -> CompileResult (Just hull) Nothing [err] + Right yul -> CompileResult (Just hull) (Just yul) [] + +renderObjects :: [Hull.Object] -> String +renderObjects = unlines . map pretty + +-- | Translate all hull objects to Yul, concatenating them. Fails with the first +-- Hull/Yul type error encountered. +objectsToYul :: [Hull.Object] -> IO (Either String String) +objectsToYul objs = do + results <- mapM objectToYul objs + pure (intercalate "\n\n" <$> sequence results) diff --git a/src/Solcore/Backend/MastEval.hs b/src/Solcore/Backend/MastEval.hs index 4eadf3e20..f1dde0933 100644 --- a/src/Solcore/Backend/MastEval.hs +++ b/src/Solcore/Backend/MastEval.hs @@ -35,12 +35,8 @@ where import Control.Monad.Reader import Control.Monad.State -import Crypto.Hash (Digest, hash) -import Crypto.Hash.Algorithms (Keccak_256) import Data.Bits (complement, shiftL, shiftR, xor, (.&.), (.|.)) -import Data.ByteArray qualified as BA import Data.ByteString qualified as BS -import Data.List (foldl') import Data.Map.Strict qualified as Map import Data.Set qualified as Set import Data.Text qualified as T @@ -51,6 +47,7 @@ import Language.Yul (YLiteral (..), YulExp (..), YulStmt (..)) import Solcore.Backend.Mast import Solcore.Frontend.Syntax.Name import Solcore.Frontend.Syntax.Stmt (Literal (..)) +import Solcore.Util.Keccak (keccak256) import Solcore.Primitives.Primitives (integerPrimNames) ----------------------------------------------------------------------- @@ -449,10 +446,7 @@ evalPrimitive (Name "strlenLit") [MastLit (StrLit s)] = in Just (MastLit (IntLit (toInteger (BS.length bs)))) evalPrimitive (Name "keccakLit") [MastLit (StrLit s)] = let bs = TE.encodeUtf8 (T.pack s) - digest :: Digest Keccak_256 - digest = hash bs - digestBytes :: BS.ByteString - digestBytes = BA.convert digest + digestBytes = keccak256 bs in Just (MastLit (IntLit (bsToIntegerBE digestBytes))) -- Integer (comptime-only, unlimited precision) primitives: evalPrimitive (Name "wordToInteger") [MastLit (IntLit n)] = diff --git a/src/Solcore/Frontend/Module/Loader.hs b/src/Solcore/Frontend/Module/Loader.hs index 9b97b2aed..d7ac3d55f 100644 --- a/src/Solcore/Frontend/Module/Loader.hs +++ b/src/Solcore/Frontend/Module/Loader.hs @@ -3,6 +3,7 @@ module Solcore.Frontend.Module.Loader LoadedModule (..), ModuleTypeCheckSurface (..), loadModuleGraph, + loadModuleGraphFromSource, moduleValidationTopDeclSegments, moduleSourcePath, moduleLocalTypeCheckSurface, @@ -23,9 +24,45 @@ import Solcore.Frontend.Module.Identity qualified as Mod import Solcore.Frontend.Parser.SolcoreParser (parseCompUnit) import Solcore.Frontend.Syntax.Name import Solcore.Frontend.Syntax.SyntaxTree +import Solcore.Std.Bundle (stdSources) import System.Directory (doesFileExist, makeAbsolute) import System.FilePath +-- | Pluggable source access for the loader. The real compiler backs this with +-- the file system; the in-memory / browser path backs it with a map of virtual +-- files. All of the loader's path-resolution logic is pure and works unchanged +-- over either. +data SourceFS + = SourceFS + { fsReadFile :: FilePath -> IO (Either String String), + fsDoesFileExist :: FilePath -> IO Bool, + fsMakeAbsolute :: FilePath -> IO FilePath + } + +-- | Source access backed by the real file system. +realSourceFS :: SourceFS +realSourceFS = + SourceFS + { fsReadFile = \path -> Right <$> readFile path, + fsDoesFileExist = doesFileExist, + fsMakeAbsolute = makeAbsolute + } + +-- | Source access backed by an in-memory map of @normalised path -> contents@. +-- Paths are normalised so lookups are insensitive to @.@/@//@ noise. +mapSourceFS :: Map FilePath String -> SourceFS +mapSourceFS files = + SourceFS + { fsReadFile = \path -> + pure $ + maybe + (Left ("in-memory source not found: " ++ path)) + Right + (Map.lookup (normalise path) files), + fsDoesFileExist = \path -> pure (Map.member (normalise path) files), + fsMakeAbsolute = pure . normalise + } + data LoadedModule = LoadedModule { loadedSourcePath :: FilePath, @@ -38,7 +75,8 @@ data LoaderConfig = LoaderConfig { mainRoot :: FilePath, stdRoot :: Maybe FilePath, - externalRoots :: Map Name FilePath + externalRoots :: Map Name FilePath, + loaderFS :: SourceFS } data LoadState @@ -76,9 +114,12 @@ data ModuleTypeCheckSurface deriving (Eq, Show) loadModuleGraph :: FilePath -> Maybe FilePath -> [(Name, FilePath)] -> FilePath -> IO (Either String ModuleGraph) -loadModuleGraph mainRootPath stdRootPath externalLibs entryFile = runExceptT do - entryAbsolute <- liftIO $ makeAbsolute entryFile - cfg <- liftIO $ mkLoaderConfig mainRootPath stdRootPath externalLibs entryFile +loadModuleGraph = loadModuleGraphWith realSourceFS + +loadModuleGraphWith :: SourceFS -> FilePath -> Maybe FilePath -> [(Name, FilePath)] -> FilePath -> IO (Either String ModuleGraph) +loadModuleGraphWith fs mainRootPath stdRootPath externalLibs entryFile = runExceptT do + entryAbsolute <- liftIO $ fsMakeAbsolute fs entryFile + cfg <- liftIO $ mkLoaderConfig fs mainRootPath stdRootPath externalLibs entryFile entryId <- moduleIdForPath Mod.MainLibrary (mainRoot cfg) entryAbsolute st <- execStateT (visit cfg entryId entryAbsolute) emptyLoadState let loaded = loadedModules st @@ -97,15 +138,37 @@ loadModuleGraph mainRootPath stdRootPath externalLibs entryFile = runExceptT do interfaces <- ExceptT $ pure (buildPublicInterfaceCache graph) pure graph {publicInterfaceCache = interfaces} -mkLoaderConfig :: FilePath -> Maybe FilePath -> [(Name, FilePath)] -> FilePath -> IO LoaderConfig -mkLoaderConfig mainRootPath stdRootPath externalLibs _entryFile = do - mainRoot' <- makeAbsolute mainRootPath - stdRoot' <- traverse makeAbsolute stdRootPath +-- | Build a module graph from editor source text with no file-system access. +-- +-- The source is mounted as @/Main.solc@ and the embedded standard library +-- under @/std@, so @import std;@ and friends resolve against the in-memory +-- bundle exactly as they would on disk. Simple contracts that don't touch std +-- (e.g. @00answer.solc@) compile without importing anything. +inMemoryMainPath :: FilePath +inMemoryMainPath = "/Main.solc" + +loadModuleGraphFromSource :: String -> IO (Either String ModuleGraph) +loadModuleGraphFromSource content = + loadModuleGraphWith fs mainRootPath (Just stdRootPath) [] inMemoryMainPath + where + mainRootPath = "/" + stdRootPath = "/std" + fs = mapSourceFS (Map.fromList (mainEntry : bundledStd)) + mainEntry = (normalise inMemoryMainPath, content) + bundledStd = + [ (normalise (stdRootPath stdFileName), source) + | (stdFileName, source) <- stdSources + ] + +mkLoaderConfig :: SourceFS -> FilePath -> Maybe FilePath -> [(Name, FilePath)] -> FilePath -> IO LoaderConfig +mkLoaderConfig fs mainRootPath stdRootPath externalLibs _entryFile = do + mainRoot' <- fsMakeAbsolute fs mainRootPath + stdRoot' <- traverse (fsMakeAbsolute fs) stdRootPath externalRoots' <- Map.fromList <$> mapM ( \(libName, libRoot) -> do - absRoot <- makeAbsolute libRoot + absRoot <- fsMakeAbsolute fs libRoot pure (libName, absRoot) ) externalLibs @@ -113,7 +176,8 @@ mkLoaderConfig mainRootPath stdRootPath externalLibs _entryFile = do LoaderConfig { mainRoot = mainRoot', stdRoot = stdRoot', - externalRoots = externalRoots' + externalRoots = externalRoots', + loaderFS = fs } visit :: @@ -126,7 +190,7 @@ visit cfg moduleId sourcePath = do loading <- gets (Set.member moduleId . loadingModules) unless (alreadyLoaded || loading) do modify (\st -> st {loadingModules = Set.insert moduleId (loadingModules st)}) - content <- liftIO (readFile sourcePath) + content <- either throwError pure =<< liftIO (fsReadFile (loaderFS cfg) sourcePath) parsed <- liftIO (parseCompUnit content) cunit <- either throwError pure parsed importedModules <- mapM (resolveImportPath cfg moduleId) (imports cunit) @@ -170,7 +234,7 @@ resolveModuleReference :: StateT LoadState (ExceptT String IO) (ModulePath, Mod.ModuleId, FilePath) resolveModuleReference cfg currentModule refKind modulePath = do candidates <- either throwError pure (resolveModuleImportCandidates cfg currentModule modulePath) - resolved <- liftIO $ firstExisting candidates + resolved <- liftIO $ firstExisting (loaderFS cfg) candidates case resolved of Just (targetId, targetPath) -> pure (modulePath, targetId, targetPath) Nothing -> @@ -215,11 +279,11 @@ resolveModuleImportCandidates cfg currentModule path = let stdName = normalizeStdModuleName modName in (Mod.ModuleId Mod.StdLibrary stdName, toFilePath root stdName) -firstExisting :: [(Mod.ModuleId, FilePath)] -> IO (Maybe (Mod.ModuleId, FilePath)) -firstExisting [] = pure Nothing -firstExisting (candidate@(_, path) : rest) = do - exists <- doesFileExist path - if exists then pure (Just candidate) else firstExisting rest +firstExisting :: SourceFS -> [(Mod.ModuleId, FilePath)] -> IO (Maybe (Mod.ModuleId, FilePath)) +firstExisting _ [] = pure Nothing +firstExisting fs (candidate@(_, path) : rest) = do + exists <- fsDoesFileExist fs path + if exists then pure (Just candidate) else firstExisting fs rest isStdSpecial :: Name -> Bool isStdSpecial (Name "std") = True diff --git a/src/Solcore/Pipeline/SolcorePipeline.hs b/src/Solcore/Pipeline/SolcorePipeline.hs index ca3fabcbc..40b2b3617 100644 --- a/src/Solcore/Pipeline/SolcorePipeline.hs +++ b/src/Solcore/Pipeline/SolcorePipeline.hs @@ -62,18 +62,25 @@ pipeline = do -- Version that returns Either for testing compile :: Option -> IO (Either String [Hull.Object]) compile opts = runExceptT $ do - let verbose = optVerbose opts - noMatchCompiler = optNoMatchCompiler opts - noIfDesugar = optNoIfDesugar opts - timeItNamed :: String -> IO a -> IO a - timeItNamed = optTimeItNamed opts - file = fileName opts + let file = fileName opts mainRoot <- liftIO $ makeAbsolute (optRootDir opts) stdRoot <- ExceptT $ pure (parseStdRoot (optImportDirs opts)) externalLibs <- ExceptT $ pure (parseExternalLibSpecs (optExternalLibs opts)) -- Parsing and import loading graph <- ExceptT $ loadModuleGraph mainRoot stdRoot externalLibs file + compileGraph opts graph + +-- Run the pipeline on an already-loaded module graph. The graph's origin (on +-- disk or built in memory from source text) is irrelevant here, so this is the +-- shared core used by both the file-based CLI and the in-memory API. +compileGraph :: Option -> ModuleGraph -> ExceptT String IO [Hull.Object] +compileGraph opts graph = do + let verbose = optVerbose opts + noMatchCompiler = optNoMatchCompiler opts + noIfDesugar = optNoIfDesugar opts + timeItNamed :: String -> IO a -> IO a + timeItNamed = optTimeItNamed opts -- Validate each module against only its own direct imports. forM_ (moduleOrder graph) $ \moduleId -> do diff --git a/src/Solcore/Std/Bundle.hs b/src/Solcore/Std/Bundle.hs new file mode 100644 index 000000000..113a95fc8 --- /dev/null +++ b/src/Solcore/Std/Bundle.hs @@ -0,0 +1,24 @@ +{-# LANGUAGE TemplateHaskell #-} + +-- | The standard library, embedded as in-memory source so it can be mounted +-- into the virtual file system used by the in-memory compiler. +-- +-- The import closure of @std@ is @std -> opcodes@ and @dispatch -> std, +-- opcodes@, so these three files form a self-contained set. Simple contracts +-- (see @test/examples/spec/00answer.solc@) use none of this and can be +-- compiled with dispatch generation disabled (@-g@). +module Solcore.Std.Bundle + ( stdSources, + ) +where + +import Solcore.Std.Embed (embedStringFile) + +-- | Standard-library modules as @(file name, source)@ pairs. File names are +-- relative to the std root the loader mounts them under. +stdSources :: [(FilePath, String)] +stdSources = + [ ("std.solc", $(embedStringFile "std/std.solc")), + ("opcodes.solc", $(embedStringFile "std/opcodes.solc")), + ("dispatch.solc", $(embedStringFile "std/dispatch.solc")) + ] diff --git a/src/Solcore/Std/Embed.hs b/src/Solcore/Std/Embed.hs new file mode 100644 index 000000000..ad65bd476 --- /dev/null +++ b/src/Solcore/Std/Embed.hs @@ -0,0 +1,19 @@ +-- | Compile-time embedding of source files as string literals. +-- +-- Used to bake the standard library into the binary so the in-memory / browser +-- compiler has no runtime file-system dependency. +module Solcore.Std.Embed + ( embedStringFile, + ) +where + +import Language.Haskell.TH (Exp, Q) +import Language.Haskell.TH.Syntax (addDependentFile, lift, runIO) + +-- | Splice the contents of a file (read at compile time) as a 'String' +-- literal. The file is registered as a dependency so edits trigger a rebuild. +embedStringFile :: FilePath -> Q Exp +embedStringFile path = do + addDependentFile path + contents <- runIO (readFile path) + lift contents diff --git a/src/Solcore/Util/Keccak.hs b/src/Solcore/Util/Keccak.hs new file mode 100644 index 000000000..971cf2d06 --- /dev/null +++ b/src/Solcore/Util/Keccak.hs @@ -0,0 +1,122 @@ +{-# LANGUAGE BangPatterns #-} + +-- | A pure-Haskell Keccak-256 (the Ethereum variant, i.e. original Keccak +-- padding with the 0x01 domain byte, not FIPS-202 SHA3-256's 0x06). +-- +-- This replaces the C-based cryptonite dependency so the compiler can be +-- cross-compiled to JavaScript with the GHC JS backend. It is only used at +-- compile time to evaluate @keccakLit@, so performance is not a concern. +module Solcore.Util.Keccak + ( keccak256, + ) +where + +import Data.Array.Unboxed (UArray, listArray, (!), (//)) +import Data.Array.Unboxed qualified as A +import Data.Bits (complement, rotateL, shiftL, shiftR, xor, (.&.), (.|.)) +import Data.ByteString (ByteString) +import Data.ByteString qualified as BS +import Data.Word (Word64) + +-- | Keccak-256 digest (32 bytes) of a byte string. +keccak256 :: ByteString -> ByteString +keccak256 = squeeze . foldl' absorbBlock initialState . chunksOf rateBytes . pad rateBytes + +rateBytes :: Int +rateBytes = 136 -- 1088-bit rate for Keccak-256 (17 lanes) + +type State = UArray Int Word64 + +initialState :: State +initialState = listArray (0, 24) (replicate 25 0) + +-- Lane index for the 5x5 state, column-major so that idx x y = x + 5*y. +idx :: Int -> Int -> Int +idx x y = x + 5 * y + +-- Multi-rate padding pad10*1 with Keccak's 0x01 domain byte. +pad :: Int -> ByteString -> ByteString +pad rate msg = msg <> padding + where + q = rate - (BS.length msg `mod` rate) + padding + | q == 1 = BS.singleton 0x81 + | otherwise = BS.singleton 0x01 <> BS.replicate (q - 2) 0x00 <> BS.singleton 0x80 + +chunksOf :: Int -> ByteString -> [ByteString] +chunksOf n bs + | BS.null bs = [] + | otherwise = BS.take n bs : chunksOf n (BS.drop n bs) + +-- Read 8 little-endian bytes at byte offset (8*lane) of a rate block. +laneAt :: ByteString -> Int -> Word64 +laneAt block lane = + foldl' (\ !acc k -> acc .|. (fromIntegral (BS.index block (base + k)) `shiftL` (8 * k))) 0 [0 .. 7] + where + base = 8 * lane + +absorbBlock :: State -> ByteString -> State +absorbBlock st block = keccakF (st // [(i, (st ! i) `xor` laneAt block i) | i <- [0 .. 16]]) + +squeeze :: State -> ByteString +squeeze st = + BS.pack + [fromIntegral ((st ! lane) `shiftR` (8 * byte)) | lane <- [0 .. 3], byte <- [0 .. 7]] + +keccakF :: State -> State +keccakF s0 = foldl' applyRound s0 roundConstants + where + applyRound a rc = + let cs = listArray (0, 4) [foldl' xor 0 [a ! idx x y | y <- [0 .. 4]] | x <- [0 .. 4]] :: UArray Int Word64 + ds = listArray (0, 4) [(cs ! ((x + 4) `mod` 5)) `xor` rotateL (cs ! ((x + 1) `mod` 5)) 1 | x <- [0 .. 4]] :: UArray Int Word64 + theta = A.array (0, 24) [(idx x y, (a ! idx x y) `xor` (ds ! x)) | x <- [0 .. 4], y <- [0 .. 4]] :: UArray Int Word64 + b = + A.array + (0, 24) + [(idx y ((2 * x + 3 * y) `mod` 5), rotateL (theta ! idx x y) (rhoOffsets ! idx x y)) | x <- [0 .. 4], y <- [0 .. 4]] + :: UArray Int Word64 + chi = + A.array + (0, 24) + [(idx x y, (b ! idx x y) `xor` (complement (b ! idx ((x + 1) `mod` 5) y) .&. (b ! idx ((x + 2) `mod` 5) y))) | x <- [0 .. 4], y <- [0 .. 4]] + :: UArray Int Word64 + in chi // [(0, (chi ! 0) `xor` rc)] + +rhoOffsets :: UArray Int Int +rhoOffsets = + listArray + (0, 24) + [ 0, 1, 62, 28, 27, + 36, 44, 6, 55, 20, + 3, 10, 43, 25, 39, + 41, 45, 15, 21, 8, + 18, 2, 61, 56, 14 + ] + +roundConstants :: [Word64] +roundConstants = + [ 0x0000000000000001, + 0x0000000000008082, + 0x800000000000808A, + 0x8000000080008000, + 0x000000000000808B, + 0x0000000080000001, + 0x8000000080008081, + 0x8000000000008009, + 0x000000000000008A, + 0x0000000000000088, + 0x0000000080008009, + 0x000000008000000A, + 0x000000008000808B, + 0x800000000000008B, + 0x8000000000008089, + 0x8000000000008003, + 0x8000000000008002, + 0x8000000000000080, + 0x000000000000800A, + 0x800000008000000A, + 0x8000000080008081, + 0x8000000000008080, + 0x0000000080000001, + 0x8000000080008008 + ] diff --git a/test/InMemoryApiTests.hs b/test/InMemoryApiTests.hs new file mode 100644 index 000000000..0680961fb --- /dev/null +++ b/test/InMemoryApiTests.hs @@ -0,0 +1,48 @@ +module InMemoryApiTests where + +import Data.List (isInfixOf) +import Solcore.Api (CompileResult (..), compileSolcore, defaultOptions) +import Solcore.Pipeline.Options (Option (..)) +import Test.Tasty +import Test.Tasty.HUnit + +-- A simple contract that uses no std (compiled with dispatch generation off, +-- i.e. the CLI's -g). Mirrors test/examples/spec/00answer.solc. +simpleSource :: String +simpleSource = + unlines + [ "contract Answer {", + " public function main() -> word {", + " return 42;", + " }", + "}" + ] + +noDispatch :: Option +noDispatch = defaultOptions {optNoGenDispatch = True} + +inMemoryApiTests :: TestTree +inMemoryApiTests = + testGroup + "In-memory API" + [ testCase "compiles a simple std-free contract with -g (hull + yul)" $ do + result <- compileSolcore noDispatch simpleSource + compileErrors result @?= [] + assertBool "expected non-empty hull output" (maybe False (not . null) (compileOutput result)) + assertBool "expected non-empty yul output" (maybe False (not . null) (compileYul result)) + assertBool "yul should be a Yul object" + (maybe False (\y -> "object" `isInfixOf` y) (compileYul result)), + -- The canonical dispatch example, fed in as source text (as the UI would) + -- and compiled with the standard library resolved from the in-memory + -- bundle. It imports std, std.dispatch and std.opcodes. + testCase "compiles the dispatch/basic.solc example against bundled std" $ do + source <- readFile "test/examples/dispatch/basic.solc" + result <- compileSolcore defaultOptions source + compileErrors result @?= [] + assertBool "expected non-empty hull output" (maybe False (not . null) (compileOutput result)) + assertBool "expected non-empty yul output" (maybe False (not . null) (compileYul result)), + testCase "reports a parse error as a diagnostic" $ do + result <- compileSolcore defaultOptions "this is not solcore" + compileOutput result @?= Nothing + assertBool "expected diagnostics" (not (null (compileErrors result))) + ] diff --git a/test/KeccakTests.hs b/test/KeccakTests.hs new file mode 100644 index 000000000..8c8a64863 --- /dev/null +++ b/test/KeccakTests.hs @@ -0,0 +1,29 @@ +module KeccakTests where + +import Data.ByteString qualified as BS +import Data.ByteString.Char8 qualified as C8 +import Numeric (showHex) +import Solcore.Util.Keccak (keccak256) +import Test.Tasty +import Test.Tasty.HUnit + +hexOf :: BS.ByteString -> String +hexOf = concatMap byte . BS.unpack + where + byte b = let h = showHex b "" in if length h == 1 then '0' : h else h + +-- Canonical, independently-published Keccak-256 (Ethereum variant) vectors. +-- These cover the single-block absorb path, which is the only one the compiler +-- exercises (keccakLit hashes short function-signature strings). +keccakTests :: TestTree +keccakTests = + testGroup + "Keccak-256" + [ testCase "empty string" $ + hexOf (keccak256 (C8.pack "")) @?= "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + testCase "abc" $ + hexOf (keccak256 (C8.pack "abc")) @?= "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45", + testCase "pangram" $ + hexOf (keccak256 (C8.pack "The quick brown fox jumps over the lazy dog")) + @?= "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15" + ] diff --git a/test/Main.hs b/test/Main.hs index 33c67549b..6489b8d66 100644 --- a/test/Main.hs +++ b/test/Main.hs @@ -3,6 +3,8 @@ module Main where import Cases import ContractAbiTests import HullCases +import InMemoryApiTests +import KeccakTests import MatchCompilerTests import ModuleTypeCheckTests import ParserTests @@ -31,5 +33,7 @@ tests = matchTests, yulEvalTests, hullTests, - specialiseTests + specialiseTests, + inMemoryApiTests, + keccakTests ] diff --git a/web/Main.hs b/web/Main.hs new file mode 100644 index 000000000..87bccd875 --- /dev/null +++ b/web/Main.hs @@ -0,0 +1,58 @@ +{-# LANGUAGE JavaScriptFFI #-} + +-- | GHCJS FFI boundary: exposes the in-memory solcore compiler to JavaScript. +-- +-- On startup it installs @globalThis.compileSolcore(source, flags)@, a +-- synchronous function that takes the source text and a flags object (the UI's +-- checkboxes) and returns @{ ok, output, errors }@. +module Main where + +import Data.List (intercalate) +import GHC.JS.Foreign.Callback (Callback, syncCallback2') +import GHC.JS.Prim (JSVal, fromJSString, toJSString) +import Solcore.Api (CompileResult (..), compileSolcore, defaultOptions) +import Solcore.Pipeline.Options (Option (..)) + +foreign import javascript "((f) => { globalThis.compileSolcore = f; })" + registerCompile :: Callback (JSVal -> JSVal -> IO JSVal) -> IO () + +-- | Read a boolean property from a JS object (missing / falsy => False). +foreign import javascript "((obj, key) => (obj && obj[key] ? 1 : 0))" + js_boolField :: JSVal -> JSVal -> IO Int + +-- | Build the JS result object returned to the caller. +foreign import javascript "((ok, output, yul, errors) => ({ ok: ok !== 0, output: output, yul: yul, errors: errors }))" + js_result :: Int -> JSVal -> JSVal -> JSVal -> IO JSVal + +boolField :: JSVal -> String -> IO Bool +boolField obj key = (/= 0) <$> js_boolField obj (toJSString key) + +-- | Map the flags object (UI checkboxes) onto compiler options. +optionsFromFlags :: JSVal -> IO Option +optionsFromFlags flags = do + noGenDispatch <- boolField flags "noGenDispatch" + noSpec <- boolField flags "noSpec" + noMatchCompiler <- boolField flags "noMatchCompiler" + noIfDesugar <- boolField flags "noIfDesugar" + noDesugarCalls <- boolField flags "noDesugarCalls" + pure + defaultOptions + { optNoGenDispatch = noGenDispatch, + optNoSpec = noSpec, + optNoMatchCompiler = noMatchCompiler, + optNoIfDesugar = noIfDesugar, + optNoDesugarCalls = noDesugarCalls + } + +compile :: JSVal -> JSVal -> IO JSVal +compile sourceVal flagsVal = do + opts <- optionsFromFlags flagsVal + result <- compileSolcore opts (fromJSString sourceVal) + case result of + CompileResult (Just output) yul _ -> + js_result 1 (toJSString output) (toJSString (maybe "" id yul)) (toJSString "") + CompileResult Nothing _ errors -> + js_result 0 (toJSString "") (toJSString "") (toJSString (intercalate "\n\n" errors)) + +main :: IO () +main = syncCallback2' compile >>= registerCompile diff --git a/web/build.sh b/web/build.sh new file mode 100755 index 000000000..04857c8f2 --- /dev/null +++ b/web/build.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# Build the in-browser solcore compiler and assemble a servable site in web/site. +# Run inside the ghcjs `nix develop` shell. +# +# ./web/build.sh dev build (-O0, fast, unminified) +# ./web/build.sh --release release (-O1, esbuild-minified, + precompressed .gz) +set -euo pipefail + +cd "$(dirname "$0")/.." + +release=0 +[ "${1:-}" = "--release" ] && release=1 + +if [ "$release" = 1 ]; then + project=cabal-ghcjs-o1.project + builddir=dist-ghcjs-o1 +else + project=cabal-ghcjs.project + builddir=dist-ghcjs +fi + +# Wipe the build dir when package/project metadata changed since the last +# successful build. Cabal's incremental multi-package plan doesn't survive a +# change in module lists and fails with confusing package-id conflicts, so a +# clean rebuild is required in that case (and only that case). +stamp="$builddir/.build-stamp" +metadata=(sol-core.cabal web/solcore-web.cabal "$project") +clean=0 +if [ ! -f "$stamp" ]; then + clean=1 +else + for f in "${metadata[@]}"; do + [ "$f" -nt "$stamp" ] && clean=1 && break + done +fi +if [ "$clean" = 1 ] && [ -d "$builddir" ]; then + echo "metadata changed since last build — cleaning $builddir" + rm -rf "$builddir" +fi + +cabal build exe:solcore-web --project-file="$project" --builddir="$builddir" +# Record a successful configure/build so the next run only cleans on real changes. +touch "$stamp" +jsexe="$(cabal list-bin exe:solcore-web --project-file="$project" --builddir="$builddir").jsexe" + +rm -rf web/site +mkdir -p web/site +cp web/index.html web/site/index.html # the React IDE (entry point) +cp web/simple.html web/site/simple.html # the minimal two-textarea page +cp web/worker.js web/site/worker.js +cp -r web/vendor web/site/vendor # local React UMD (no CDN dependency) + +# Precompile the IDE's JSX to plain JS with esbuild (no in-browser Babel). +npx --yes esbuild@0.24.0 web/ide.jsx --jsx=transform --minify --outfile=web/site/ide.js + +if [ "$release" = 1 ]; then + # Minify with esbuild (fetched on demand via npx). The win over raw GHCJS + # output is small — the codegen is already compact — so fall back gracefully. + if npx --yes esbuild@0.24.0 "$jsexe/all.js" --minify --outfile=web/site/all.js 2>/dev/null; then + echo "minified all.js with esbuild" + else + echo "warning: esbuild unavailable, using unminified all.js" >&2 + cp "$jsexe/all.js" web/site/all.js + fi + # Precompress for hosts serving with `precompressed gzip` (e.g. Caddy). + gzip -9 -kf web/site/all.js +else + cp "$jsexe/all.js" web/site/all.js +fi + +mode=$([ "$release" = 1 ] && echo "release (-O1, minified)" || echo "dev (-O0)") +echo "Built: $mode" +ls -la web/site/all.js web/site/all.js.gz 2>/dev/null | awk '{print " " $5 " " $NF}' +echo "Serve with: (cd web/site && python3 -m http.server 8000)" +echo " React IDE (main): http://localhost:8000/index.html" +echo " simple page: http://localhost:8000/simple.html" diff --git a/web/ide.jsx b/web/ide.jsx new file mode 100644 index 000000000..75ac6d6ac --- /dev/null +++ b/web/ide.jsx @@ -0,0 +1,206 @@ +// solcore IDE — React tabs/file-tree frontend over the GHCJS solcore backend. +// Compiled to plain JS by esbuild at build time (see web/build.sh); React and +// ReactDOM are provided as globals by the vendored UMD scripts. Compilation +// runs in a Web Worker (web/worker.js) so the main thread stays free to render +// a live timer. +const { useState, useReducer, useCallback, useEffect, useRef } = React; + +// ---- seed virtual file system (FileType nodes, mirrors remix-ui/workspace) ---- +// A node: { name, path, isDirectory, children? , content? } +const seedTree = { + name: "workspace", path: "", isDirectory: true, children: [ + { name: "examples", path: "examples", isDirectory: true, children: [ + { name: "Answer.solc", path: "examples/Answer.solc", isDirectory: false, + content: "contract Answer {\n public function main() -> word {\n return 42;\n }\n}\n" }, + { name: "Counter.solc", path: "examples/Counter.solc", isDirectory: false, + content: "import std.{*};\nimport std.dispatch.{*};\n\ncontract Counter {\n counter : uint256;\n\n constructor() { counter = uint256(42); }\n\n public function get() -> uint256 {\n return counter;\n }\n}\n" }, + { name: "basic.solc", path: "examples/basic.solc", isDirectory: false, + content: "import std.{*};\nimport std.dispatch.{*};\nimport std.opcodes.{address as address_};\n\nfunction self() -> address {\n return address(address_());\n}\n\ncontract C {\n constructor() {}\n public function nothing() -> () {}\n\n // Re-enters this very contract via raw_call(address(this), ...). The payload\n // is the 4-byte selector of an existing entry point (something(), 0xa7a0d537),\n // built by left-aligning it in a bytes32 and truncating to 4 bytes. The inner\n // call succeeds, so raw_call reports ok == true and returns its returndata\n // (the abi-encoded uint256(1)).\n public function callSelf() -> (bool, memory(bytes)) {\n let sel: bytes32 = bytes32(0xa7a0d53700000000000000000000000000000000000000000000000000000000);\n let payload = truncate(to_bytes(sel), 4);\n match raw_call(self(), uint256(0), payload) {\n | (ok, ret) => return (ok, ret);\n }\n }\n\n // Same shape, but the selector (0xdeadc0de) matches no entry point, so dispatch\n // reverts (there is no fallback). raw_call swallows the inner revert and reports\n // ok == false; this outer call itself still succeeds and returns the revert\n // returndata (the 4-byte NoFallback error selector).\n public function callSelfInvalid() -> (bool, memory(bytes)) {\n let sel: bytes32 = bytes32(0xdeadc0de00000000000000000000000000000000000000000000000000000000);\n let payload = truncate(to_bytes(sel), 4);\n match raw_call(self(), uint256(0), payload) {\n | (ok, ret) => return (ok, ret);\n }\n }\n\n public function something() -> (uint256) {\n return uint256(1);\n }\n\n public function add2(x : uint256, y : uint256) -> uint256 {\n return Add.add(x,y);\n }\n\n public function add3(x : uint256, y : uint256, z : uint256) -> uint256 {\n return Add.add(z, Add.add(x,y));\n }\n\n public function addmod3(x : uint256, y : uint256, k : uint256) -> uint256 {\n return addmod(x, y, k);\n }\n\n public function mulmod3(x : uint256, y : uint256, k : uint256) -> uint256 {\n return mulmod(x, y, k);\n }\n\n // Bitwise / modulo via the syntactic sugar only (no explicit class calls):\n // `^` -> BitXor.bxor, `|` -> BitOr.bor, `&` -> BitAnd.band, `%` -> Mod.mod.\n public function bxor2(x : uint256, y : uint256) -> uint256 {\n return x ^ y;\n }\n\n public function bor2(x : uint256, y : uint256) -> uint256 {\n return x | y;\n }\n\n public function band2(x : uint256, y : uint256) -> uint256 {\n return x & y;\n }\n\n public function mod2(x : uint256, y : uint256) -> uint256 {\n return x % y;\n }\n\n public function id_bytes(b: memory(bytes)) -> memory(bytes) {\n return b;\n }\n\n public function id_string(b: memory(string)) -> memory(string) {\n return b;\n }\n\n public function id_bytes32(b: bytes32) -> bytes32 {\n return b;\n }\n\n public function id_address(a: address) -> address {\n return a;\n }\n\n public function id_pair() -> (uint256, uint256) {\n return (uint256(7), uint256(11));\n }\n\n function hidden() -> (uint256) {\n return uint256(42);\n }\n}\n" }, + ]}, + { name: "Scratch.solc", path: "Scratch.solc", isDirectory: false, + content: "contract Scratch {\n public function main() -> word {\n return 7;\n }\n}\n" }, + ] +}; + +// Collect path -> content for all files, for the mutable editor buffers. +function collectFiles(node, acc) { + if (node.isDirectory) node.children.forEach(c => collectFiles(c, acc)); + else acc[node.path] = node.content; + return acc; +} + +const basename = (p) => p.split("/").pop(); + +// ---- tab controller (mirrors apps/remix-ide tab-proxy: loadedTabs + open/close/select) ---- +const initialTabs = { open: [], active: null }; +function tabsReducer(state, action) { + switch (action.type) { + case "OPEN": { + const open = state.open.includes(action.path) ? state.open : [...state.open, action.path]; + return { open, active: action.path }; + } + case "SELECT": + return { ...state, active: action.path }; + case "CLOSE": { + const idx = state.open.indexOf(action.path); + if (idx === -1) return state; + const open = state.open.filter(p => p !== action.path); + let active = state.active; + if (state.active === action.path) { // pick a neighbour, like tab-proxy + active = open[idx - 1] || open[idx] || null; + } + return { open, active }; + } + default: return state; + } +} + +// ---- tree view (mirrors remix-ui/tree-view: recursive ul/li with caret) ---- +function TreeNode({ node, depth, activePath, onOpen }) { + const [expanded, setExpanded] = useState(true); + const pad = { paddingLeft: depth * 12 }; + if (node.isDirectory) { + return ( +
  • +
    setExpanded(!expanded)}> + {expanded ? "▾" : "▸"}{node.name} +
    + {expanded && ( +
      {node.children.map(c => + )} +
    + )} +
  • + ); + } + return ( +
  • +
    onOpen(node.path)}> + 📄{node.name} +
    +
  • + ); +} + +// ---- tabs bar (mirrors remix-ui/tabs: title + middle-click / × to close) ---- +function TabsBar({ open, active, onSelect, onClose }) { + return ( +
    + {open.map(path => ( +
    onSelect(path)} + onMouseDown={(e) => { if (e.button === 1) { e.preventDefault(); onClose(path); } }}> + {basename(path)} + { e.stopPropagation(); onClose(path); }}>× +
    + ))} +
    + ); +} + +const FLAGS = [ + ["noGenDispatch", "-g no dispatch"], +]; + +function App() { + const [files, setFiles] = useState(() => collectFiles(seedTree, {})); + const [tabs, dispatch] = useReducer(tabsReducer, initialTabs); + const [flags, setFlags] = useState({ noGenDispatch: true }); + const [result, setResult] = useState({ ok: true, hull: "", yul: "", errors: "" }); + const [ready, setReady] = useState(false); + const [compiling, setCompiling] = useState(false); + const [elapsed, setElapsed] = useState(null); // ms; null before the first compile + + const workerRef = useRef(null); + const timerRef = useRef(null); + const startRef = useRef(0); + + // Spin up the compiler worker once; it posts "ready" when the bundle loads. + useEffect(() => { + const worker = new Worker("worker.js"); + workerRef.current = worker; + worker.onmessage = (e) => { + const msg = e.data; + if (msg.type === "ready") { setReady(true); return; } + if (msg.type === "result") { + clearInterval(timerRef.current); + setElapsed(performance.now() - startRef.current); // freeze final time + setCompiling(false); + setResult(msg.ok + ? { ok: true, hull: msg.output, yul: msg.yul, errors: "" } + : { ok: false, hull: "", yul: "", errors: msg.errors }); + } + }; + return () => worker.terminate(); + }, []); + + const openFile = useCallback((path) => dispatch({ type: "OPEN", path }), []); + const editActive = (content) => setFiles(f => ({ ...f, [tabs.active]: content })); + + const compile = () => { + if (!tabs.active || !ready || compiling) return; + setCompiling(true); + startRef.current = performance.now(); + setElapsed(0); + // Tick the timer while the worker compiles. + timerRef.current = setInterval(() => setElapsed(performance.now() - startRef.current), 47); + workerRef.current.postMessage({ type: "compile", source: files[tabs.active], flags }); + }; + + const status = + !ready ? "loading compiler…" + : compiling ? "compiling… " + Math.round(elapsed) + " ms" + : elapsed != null ? (result.ok ? "compiled in " : "failed after ") + Math.round(elapsed) + " ms" + : "compiler ready"; + + return ( +
    +
    + +
    + {FLAGS.map(([key, label]) => ( + + ))} +
    + {status} +
    + +
    +
      + +
    +
    + +
    + dispatch({ type: "SELECT", path: p })} + onClose={(p) => dispatch({ type: "CLOSE", path: p })} /> + {tabs.active ? ( + +
    +
    + + +
    +
    + + +
    +
    + + + + + diff --git a/web/solcore-web.cabal b/web/solcore-web.cabal new file mode 100644 index 000000000..ee7833a75 --- /dev/null +++ b/web/solcore-web.cabal @@ -0,0 +1,12 @@ +cabal-version: 3.0 +name: solcore-web +version: 0.0.0.0 +synopsis: GHCJS FFI boundary exposing the solcore compiler to the browser. +build-type: Simple + +executable solcore-web + main-is: Main.hs + hs-source-dirs: . + default-language: Haskell2010 + default-extensions: JavaScriptFFI + build-depends: base, sol-core diff --git a/web/vendor/react-dom.production.min.js b/web/vendor/react-dom.production.min.js new file mode 100644 index 000000000..fb4e099c0 --- /dev/null +++ b/web/vendor/react-dom.production.min.js @@ -0,0 +1,267 @@ +/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +(function(){/* + Modernizr 3.0.0pre (Custom Build) | MIT +*/ +'use strict';(function(Q,zb){"object"===typeof exports&&"undefined"!==typeof module?zb(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],zb):(Q=Q||self,zb(Q.ReactDOM={},Q.React))})(this,function(Q,zb){function m(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;cb}return!1}function Y(a,b,c,d,e,f,g){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f;this.removeEmptyString=g}function $d(a,b,c,d){var e=R.hasOwnProperty(b)?R[b]:null;if(null!==e?0!==e.type:d||!(2h||e[g]!==f[h]){var k="\n"+e[g].replace(" at new "," at ");a.displayName&&k.includes("")&&(k=k.replace("",a.displayName));return k}while(1<=g&&0<=h)}break}}}finally{ce=!1,Error.prepareStackTrace=c}return(a=a?a.displayName||a.name:"")?bc(a): +""}function fj(a){switch(a.tag){case 5:return bc(a.type);case 16:return bc("Lazy");case 13:return bc("Suspense");case 19:return bc("SuspenseList");case 0:case 2:case 15:return a=be(a.type,!1),a;case 11:return a=be(a.type.render,!1),a;case 1:return a=be(a.type,!0),a;default:return""}}function de(a){if(null==a)return null;if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Bb:return"Fragment";case Cb:return"Portal";case ee:return"Profiler";case fe:return"StrictMode"; +case ge:return"Suspense";case he:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case gg:return(a.displayName||"Context")+".Consumer";case hg:return(a._context.displayName||"Context")+".Provider";case ie:var b=a.render;a=a.displayName;a||(a=b.displayName||b.name||"",a=""!==a?"ForwardRef("+a+")":"ForwardRef");return a;case je:return b=a.displayName||null,null!==b?b:de(a.type)||"Memo";case Ta:b=a._payload;a=a._init;try{return de(a(b))}catch(c){}}return null}function gj(a){var b=a.type; +switch(a.tag){case 24:return"Cache";case 9:return(b.displayName||"Context")+".Consumer";case 10:return(b._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=b.render,a=a.displayName||a.name||"",b.displayName||(""!==a?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return b;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return de(b);case 8:return b===fe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler"; +case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"===typeof b)return b.displayName||b.name||null;if("string"===typeof b)return b}return null}function Ua(a){switch(typeof a){case "boolean":case "number":case "string":case "undefined":return a;case "object":return a;default:return""}}function ig(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"=== +b)}function hj(a){var b=ig(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker= +null;delete a[b]}}}}function Pc(a){a._valueTracker||(a._valueTracker=hj(a))}function jg(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=ig(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Qc(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function ke(a,b){var c=b.checked;return E({},b,{defaultChecked:void 0,defaultValue:void 0, +value:void 0,checked:null!=c?c:a._wrapperState.initialChecked})}function kg(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=Ua(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function lg(a,b){b=b.checked;null!=b&&$d(a,"checked",b,!1)}function le(a,b){lg(a,b);var c=Ua(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!= +c)a.value=""+c}else a.value!==""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?me(a,b.type,c):b.hasOwnProperty("defaultValue")&&me(a,b.type,Ua(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function mg(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue; +c||b===a.value||(a.value=b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function me(a,b,c){if("number"!==b||Qc(a.ownerDocument)!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function Db(a,b,c,d){a=a.options;if(b){b={};for(var e=0;e>>=0;return 0===a?32:31-(qj(a)/rj|0)|0}function hc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a& +4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function Vc(a,b){var c=a.pendingLanes;if(0===c)return 0;var d=0,e=a.suspendedLanes,f=a.pingedLanes,g=c&268435455;if(0!==g){var h=g&~e;0!==h?d=hc(h):(f&=g,0!==f&&(d=hc(f)))}else g=c&~e,0!==g?d=hc(g):0!==f&&(d=hc(f));if(0===d)return 0;if(0!==b&&b!==d&&0===(b&e)&& +(e=d&-d,f=b&-b,e>=f||16===e&&0!==(f&4194240)))return b;0!==(d&4)&&(d|=c&16);b=a.entangledLanes;if(0!==b)for(a=a.entanglements,b&=d;0c;c++)b.push(a); +return b}function ic(a,b,c){a.pendingLanes|=b;536870912!==b&&(a.suspendedLanes=0,a.pingedLanes=0);a=a.eventTimes;b=31-ta(b);a[b]=c}function uj(a,b){var c=a.pendingLanes&~b;a.pendingLanes=b;a.suspendedLanes=0;a.pingedLanes=0;a.expiredLanes&=b;a.mutableReadLanes&=b;a.entangledLanes&=b;b=a.entanglements;var d=a.eventTimes;for(a=a.expirationTimes;0=b)return{node:c,offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=$g(c)}}function bh(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?bh(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function ch(){for(var a=window,b=Qc();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break; +b=Qc(a.document)}return b}function Ie(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function Tj(a){var b=ch(),c=a.focusedElem,d=a.selectionRange;if(b!==c&&c&&c.ownerDocument&&bh(c.ownerDocument.documentElement,c)){if(null!==d&&Ie(c))if(b=d.start,a=d.end,void 0===a&&(a=b),"selectionStart"in c)c.selectionStart=b,c.selectionEnd=Math.min(a,c.value.length); +else if(a=(b=c.ownerDocument||document)&&b.defaultView||window,a.getSelection){a=a.getSelection();var e=c.textContent.length,f=Math.min(d.start,e);d=void 0===d.end?f:Math.min(d.end,e);!a.extend&&f>d&&(e=d,d=f,f=e);e=ah(c,f);var g=ah(c,d);e&&g&&(1!==a.rangeCount||a.anchorNode!==e.node||a.anchorOffset!==e.offset||a.focusNode!==g.node||a.focusOffset!==g.offset)&&(b=b.createRange(),b.setStart(e.node,e.offset),a.removeAllRanges(),f>d?(a.addRange(b),a.extend(g.node,g.offset)):(b.setEnd(g.node,g.offset), +a.addRange(b)))}b=[];for(a=c;a=a.parentNode;)1===a.nodeType&&b.push({element:a,left:a.scrollLeft,top:a.scrollTop});"function"===typeof c.focus&&c.focus();for(c=0;cMb||(a.current=Se[Mb],Se[Mb]=null,Mb--)} +function y(a,b,c){Mb++;Se[Mb]=a.current;a.current=b}function Nb(a,b){var c=a.type.contextTypes;if(!c)return cb;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function ea(a){a=a.childContextTypes;return null!==a&&void 0!==a}function th(a,b,c){if(J.current!==cb)throw Error(m(168)); +y(J,b);y(S,c)}function uh(a,b,c){var d=a.stateNode;b=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in b))throw Error(m(108,gj(a)||"Unknown",e));return E({},c,d)}function ld(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||cb;pb=J.current;y(J,a);y(S,S.current);return!0}function vh(a,b,c){var d=a.stateNode;if(!d)throw Error(m(169));c?(a=uh(a,b,pb),d.__reactInternalMemoizedMergedChildContext=a,v(S),v(J),y(J,a)):v(S); +y(S,c)}function wh(a){null===La?La=[a]:La.push(a)}function jk(a){md=!0;wh(a)}function db(){if(!Te&&null!==La){Te=!0;var a=0,b=z;try{var c=La;for(z=1;a>=g;e-=g;Ma=1<<32-ta(b)+e|c<t?(q=l,l=null):q=l.sibling;var A=r(e,l,h[t],k);if(null===A){null===l&&(l=q);break}a&&l&&null===A.alternate&&b(e,l);g=f(A,g,t);null===m?n=A:m.sibling=A;m=A;l=q}if(t===h.length)return c(e,l),D&&qb(e,t),n;if(null===l){for(;t< +h.length;t++)l=u(e,h[t],k),null!==l&&(g=f(l,g,t),null===m?n=l:m.sibling=l,m=l);D&&qb(e,t);return n}for(l=d(e,l);tt?(A=q,q=null):A=q.sibling;var x=r(e,q,w.value,k);if(null===x){null===q&&(q=A);break}a&&q&&null===x.alternate&&b(e,q);g=f(x,g,t);null===l?n=x:l.sibling=x;l=x;q=A}if(w.done)return c(e,q),D&&qb(e,t),n;if(null===q){for(;!w.done;t++,w=h.next())w=u(e,w.value,k),null!==w&&(g=f(w,g,t),null===l?n=w:l.sibling=w,l=w);D&&qb(e,t);return n}for(q=d(e,q);!w.done;t++,w=h.next())w=p(q,e,t,w.value,k),null!==w&&(a&&null!==w.alternate&&q.delete(null===w.key?t:w.key),g=f(w,g,t),null===l?n=w:l.sibling= +w,l=w);a&&q.forEach(function(a){return b(e,a)});D&&qb(e,t);return n}function v(a,d,f,h){"object"===typeof f&&null!==f&&f.type===Bb&&null===f.key&&(f=f.props.children);if("object"===typeof f&&null!==f){switch(f.$$typeof){case sd:a:{for(var k=f.key,n=d;null!==n;){if(n.key===k){k=f.type;if(k===Bb){if(7===n.tag){c(a,n.sibling);d=e(n,f.props.children);d.return=a;a=d;break a}}else if(n.elementType===k||"object"===typeof k&&null!==k&&k.$$typeof===Ta&&Ch(k)===n.type){c(a,n.sibling);d=e(n,f.props);d.ref=vc(a, +n,f);d.return=a;a=d;break a}c(a,n);break}else b(a,n);n=n.sibling}f.type===Bb?(d=sb(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=rd(f.type,f.key,f.props,null,a.mode,h),h.ref=vc(a,d,f),h.return=a,a=h)}return g(a);case Cb:a:{for(n=f.key;null!==d;){if(d.key===n)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=$e(f,a.mode,h);d.return=a; +a=d}return g(a);case Ta:return n=f._init,v(a,d,n(f._payload),h)}if(cc(f))return x(a,d,f,h);if(ac(f))return I(a,d,f,h);qd(a,f)}return"string"===typeof f&&""!==f||"number"===typeof f?(f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):(c(a,d),d=Ze(f,a.mode,h),d.return=a,a=d),g(a)):c(a,d)}return v}function af(){bf=Rb=td=null}function cf(a,b){b=ud.current;v(ud);a._currentValue=b}function df(a,b,c){for(;null!==a;){var d=a.alternate;(a.childLanes&b)!==b?(a.childLanes|=b,null!==d&&(d.childLanes|= +b)):null!==d&&(d.childLanes&b)!==b&&(d.childLanes|=b);if(a===c)break;a=a.return}}function Sb(a,b){td=a;bf=Rb=null;a=a.dependencies;null!==a&&null!==a.firstContext&&(0!==(a.lanes&b)&&(ha=!0),a.firstContext=null)}function qa(a){var b=a._currentValue;if(bf!==a)if(a={context:a,memoizedValue:b,next:null},null===Rb){if(null===td)throw Error(m(308));Rb=a;td.dependencies={lanes:0,firstContext:a}}else Rb=Rb.next=a;return b}function ef(a){null===tb?tb=[a]:tb.push(a)}function Eh(a,b,c,d){var e=b.interleaved; +null===e?(c.next=c,ef(b)):(c.next=e.next,e.next=c);b.interleaved=c;return Oa(a,d)}function Oa(a,b){a.lanes|=b;var c=a.alternate;null!==c&&(c.lanes|=b);c=a;for(a=a.return;null!==a;)a.childLanes|=b,c=a.alternate,null!==c&&(c.childLanes|=b),c=a,a=a.return;return 3===c.tag?c.stateNode:null}function ff(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Fh(a,b){a=a.updateQueue;b.updateQueue===a&&(b.updateQueue= +{baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function Pa(a,b){return{eventTime:a,lane:b,tag:0,payload:null,callback:null,next:null}}function fb(a,b,c){var d=a.updateQueue;if(null===d)return null;d=d.shared;if(0!==(p&2)){var e=d.pending;null===e?b.next=b:(b.next=e.next,e.next=b);d.pending=b;return kk(a,c)}e=d.interleaved;null===e?(b.next=b,ef(d)):(b.next=e.next,e.next=b);d.interleaved=b;return Oa(a,c)}function vd(a,b,c){b= +b.updateQueue;if(null!==b&&(b=b.shared,0!==(c&4194240))){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function Gh(a,b){var c=a.updateQueue,d=a.alternate;if(null!==d&&(d=d.updateQueue,c===d)){var e=null,f=null;c=c.firstBaseUpdate;if(null!==c){do{var g={eventTime:c.eventTime,lane:c.lane,tag:c.tag,payload:c.payload,callback:c.callback,next:null};null===f?e=f=g:f=f.next=g;c=c.next}while(null!==c);null===f?e=f=b:f=f.next=b}else e=f=b;c={baseState:d.baseState,firstBaseUpdate:e,lastBaseUpdate:f, +shared:d.shared,effects:d.effects};a.updateQueue=c;return}a=c.lastBaseUpdate;null===a?c.firstBaseUpdate=b:a.next=b;c.lastBaseUpdate=b}function wd(a,b,c,d){var e=a.updateQueue;gb=!1;var f=e.firstBaseUpdate,g=e.lastBaseUpdate,h=e.shared.pending;if(null!==h){e.shared.pending=null;var k=h,n=k.next;k.next=null;null===g?f=n:g.next=n;g=k;var l=a.alternate;null!==l&&(l=l.updateQueue,h=l.lastBaseUpdate,h!==g&&(null===h?l.firstBaseUpdate=n:h.next=n,l.lastBaseUpdate=k))}if(null!==f){var m=e.baseState;g=0;l= +n=k=null;h=f;do{var r=h.lane,p=h.eventTime;if((d&r)===r){null!==l&&(l=l.next={eventTime:p,lane:0,tag:h.tag,payload:h.payload,callback:h.callback,next:null});a:{var x=a,v=h;r=b;p=c;switch(v.tag){case 1:x=v.payload;if("function"===typeof x){m=x.call(p,m,r);break a}m=x;break a;case 3:x.flags=x.flags&-65537|128;case 0:x=v.payload;r="function"===typeof x?x.call(p,m,r):x;if(null===r||void 0===r)break a;m=E({},m,r);break a;case 2:gb=!0}}null!==h.callback&&0!==h.lane&&(a.flags|=64,r=e.effects,null===r?e.effects= +[h]:r.push(h))}else p={eventTime:p,lane:r,tag:h.tag,payload:h.payload,callback:h.callback,next:null},null===l?(n=l=p,k=m):l=l.next=p,g|=r;h=h.next;if(null===h)if(h=e.shared.pending,null===h)break;else r=h,h=r.next,r.next=null,e.lastBaseUpdate=r,e.shared.pending=null}while(1);null===l&&(k=m);e.baseState=k;e.firstBaseUpdate=n;e.lastBaseUpdate=l;b=e.shared.interleaved;if(null!==b){e=b;do g|=e.lane,e=e.next;while(e!==b)}else null===f&&(e.shared.lanes=0);ra|=g;a.lanes=g;a.memoizedState=m}}function Hh(a, +b,c){a=b.effects;b.effects=null;if(null!==a)for(b=0;bc?c:4;a(!0);var d=sf.transition;sf.transition= +{};try{a(!1),b()}finally{z=c,sf.transition=d}}function $h(){return sa().memoizedState}function qk(a,b,c){var d=hb(a);c={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ai(a))bi(b,c);else if(c=Eh(a,b,c,d),null!==c){var e=Z();xa(c,a,d,e);ci(c,b,d)}}function ok(a,b,c){var d=hb(a),e={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ai(a))bi(b,e);else{var f=a.alternate;if(0===a.lanes&&(null===f||0===f.lanes)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState, +h=f(g,c);e.hasEagerState=!0;e.eagerState=h;if(ua(h,g)){var k=b.interleaved;null===k?(e.next=e,ef(b)):(e.next=k.next,k.next=e);b.interleaved=e;return}}catch(n){}finally{}c=Eh(a,b,e,d);null!==c&&(e=Z(),xa(c,a,d,e),ci(c,b,d))}}function ai(a){var b=a.alternate;return a===C||null!==b&&b===C}function bi(a,b){zc=Ad=!0;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}function ci(a,b,c){if(0!==(c&4194240)){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function ya(a,b){if(a&& +a.defaultProps){b=E({},b);a=a.defaultProps;for(var c in a)void 0===b[c]&&(b[c]=a[c]);return b}return b}function tf(a,b,c,d){b=a.memoizedState;c=c(d,b);c=null===c||void 0===c?b:E({},b,c);a.memoizedState=c;0===a.lanes&&(a.updateQueue.baseState=c)}function di(a,b,c,d,e,f,g){a=a.stateNode;return"function"===typeof a.shouldComponentUpdate?a.shouldComponentUpdate(d,f,g):b.prototype&&b.prototype.isPureReactComponent?!qc(c,d)||!qc(e,f):!0}function ei(a,b,c){var d=!1,e=cb;var f=b.contextType;"object"===typeof f&& +null!==f?f=qa(f):(e=ea(b)?pb:J.current,d=b.contextTypes,f=(d=null!==d&&void 0!==d)?Nb(a,e):cb);b=new b(c,f);a.memoizedState=null!==b.state&&void 0!==b.state?b.state:null;b.updater=Dd;a.stateNode=b;b._reactInternals=a;d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=e,a.__reactInternalMemoizedMaskedChildContext=f);return b}function fi(a,b,c,d){a=b.state;"function"===typeof b.componentWillReceiveProps&&b.componentWillReceiveProps(c,d);"function"===typeof b.UNSAFE_componentWillReceiveProps&& +b.UNSAFE_componentWillReceiveProps(c,d);b.state!==a&&Dd.enqueueReplaceState(b,b.state,null)}function uf(a,b,c,d){var e=a.stateNode;e.props=c;e.state=a.memoizedState;e.refs={};ff(a);var f=b.contextType;"object"===typeof f&&null!==f?e.context=qa(f):(f=ea(b)?pb:J.current,e.context=Nb(a,f));e.state=a.memoizedState;f=b.getDerivedStateFromProps;"function"===typeof f&&(tf(a,b,f,c),e.state=a.memoizedState);"function"===typeof b.getDerivedStateFromProps||"function"===typeof e.getSnapshotBeforeUpdate||"function"!== +typeof e.UNSAFE_componentWillMount&&"function"!==typeof e.componentWillMount||(b=e.state,"function"===typeof e.componentWillMount&&e.componentWillMount(),"function"===typeof e.UNSAFE_componentWillMount&&e.UNSAFE_componentWillMount(),b!==e.state&&Dd.enqueueReplaceState(e,e.state,null),wd(a,c,e,d),e.state=a.memoizedState);"function"===typeof e.componentDidMount&&(a.flags|=4194308)}function Ub(a,b){try{var c="",d=b;do c+=fj(d),d=d.return;while(d);var e=c}catch(f){e="\nError generating stack: "+f.message+ +"\n"+f.stack}return{value:a,source:b,stack:e,digest:null}}function vf(a,b,c){return{value:a,source:null,stack:null!=c?c:null,digest:null!=b?b:null}}function wf(a,b){try{console.error(b.value)}catch(c){setTimeout(function(){throw c;})}}function gi(a,b,c){c=Pa(-1,c);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){Ed||(Ed=!0,xf=d);wf(a,b)};return c}function hi(a,b,c){c=Pa(-1,c);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){return d(e)}; +c.callback=function(){wf(a,b)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){wf(a,b);"function"!==typeof d&&(null===ib?ib=new Set([this]):ib.add(this));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ii(a,b,c){var d=a.pingCache;if(null===d){d=a.pingCache=new rk;var e=new Set;d.set(b,e)}else e=d.get(b),void 0===e&&(e=new Set,d.set(b,e));e.has(c)||(e.add(c),a=sk.bind(null,a,b,c),b.then(a,a))}function ji(a){do{var b; +if(b=13===a.tag)b=a.memoizedState,b=null!==b?null!==b.dehydrated?!0:!1:!0;if(b)return a;a=a.return}while(null!==a);return null}function ki(a,b,c,d,e){if(0===(a.mode&1))return a===b?a.flags|=65536:(a.flags|=128,c.flags|=131072,c.flags&=-52805,1===c.tag&&(null===c.alternate?c.tag=17:(b=Pa(-1,1),b.tag=2,fb(c,b,1))),c.lanes|=1),a;a.flags|=65536;a.lanes=e;return a}function aa(a,b,c,d){b.child=null===a?li(b,null,c,d):Vb(b,a.child,c,d)}function mi(a,b,c,d,e){c=c.render;var f=b.ref;Sb(b,e);d=mf(a,b,c,d,f, +e);c=nf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&c&&Ue(b);b.flags|=1;aa(a,b,d,e);return b.child}function ni(a,b,c,d,e){if(null===a){var f=c.type;if("function"===typeof f&&!yf(f)&&void 0===f.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=f,oi(a,b,f,d,e);a=rd(c.type,null,d,b,b.mode,e);a.ref=b.ref;a.return=b;return b.child=a}f=a.child;if(0===(a.lanes&e)){var g=f.memoizedProps;c=c.compare;c=null!==c?c:qc;if(c(g,d)&&a.ref=== +b.ref)return Qa(a,b,e)}b.flags|=1;a=eb(f,d);a.ref=b.ref;a.return=b;return b.child=a}function oi(a,b,c,d,e){if(null!==a){var f=a.memoizedProps;if(qc(f,d)&&a.ref===b.ref)if(ha=!1,b.pendingProps=d=f,0!==(a.lanes&e))0!==(a.flags&131072)&&(ha=!0);else return b.lanes=a.lanes,Qa(a,b,e)}return zf(a,b,c,d,e)}function pi(a,b,c){var d=b.pendingProps,e=d.children,f=null!==a?a.memoizedState:null;if("hidden"===d.mode)if(0===(b.mode&1))b.memoizedState={baseLanes:0,cachePool:null,transitions:null},y(Ga,ba),ba|=c; +else{if(0===(c&1073741824))return a=null!==f?f.baseLanes|c:c,b.lanes=b.childLanes=1073741824,b.memoizedState={baseLanes:a,cachePool:null,transitions:null},b.updateQueue=null,y(Ga,ba),ba|=a,null;b.memoizedState={baseLanes:0,cachePool:null,transitions:null};d=null!==f?f.baseLanes:c;y(Ga,ba);ba|=d}else null!==f?(d=f.baseLanes|c,b.memoizedState=null):d=c,y(Ga,ba),ba|=d;aa(a,b,e,c);return b.child}function qi(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.flags|=512,b.flags|=2097152}function zf(a, +b,c,d,e){var f=ea(c)?pb:J.current;f=Nb(b,f);Sb(b,e);c=mf(a,b,c,d,f,e);d=nf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&d&&Ue(b);b.flags|=1;aa(a,b,c,e);return b.child}function ri(a,b,c,d,e){if(ea(c)){var f=!0;ld(b)}else f=!1;Sb(b,e);if(null===b.stateNode)Fd(a,b),ei(b,c,d),uf(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var k=g.context,n=c.contextType;"object"===typeof n&&null!==n?n=qa(n):(n=ea(c)?pb:J.current,n=Nb(b, +n));var l=c.getDerivedStateFromProps,m="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate;m||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||k!==n)&&fi(b,g,d,n);gb=!1;var r=b.memoizedState;g.state=r;wd(b,d,g,e);k=b.memoizedState;h!==d||r!==k||S.current||gb?("function"===typeof l&&(tf(b,c,l,d),k=b.memoizedState),(h=gb||di(b,c,h,d,r,k,n))?(m||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount|| +("function"===typeof g.componentWillMount&&g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.flags|=4194308)):("function"===typeof g.componentDidMount&&(b.flags|=4194308),b.memoizedProps=d,b.memoizedState=k),g.props=d,g.state=k,g.context=n,d=h):("function"===typeof g.componentDidMount&&(b.flags|=4194308),d=!1)}else{g=b.stateNode;Fh(a,b);h=b.memoizedProps;n=b.type===b.elementType?h:ya(b.type,h);g.props= +n;m=b.pendingProps;r=g.context;k=c.contextType;"object"===typeof k&&null!==k?k=qa(k):(k=ea(c)?pb:J.current,k=Nb(b,k));var p=c.getDerivedStateFromProps;(l="function"===typeof p||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==m||r!==k)&&fi(b,g,d,k);gb=!1;r=b.memoizedState;g.state=r;wd(b,d,g,e);var x=b.memoizedState;h!==m||r!==x||S.current||gb?("function"===typeof p&&(tf(b,c,p,d),x=b.memoizedState), +(n=gb||di(b,c,n,d,r,x,k)||!1)?(l||"function"!==typeof g.UNSAFE_componentWillUpdate&&"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,x,k),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,x,k)),"function"===typeof g.componentDidUpdate&&(b.flags|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.flags|=1024)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|= +4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),b.memoizedProps=d,b.memoizedState=x),g.props=d,g.state=x,g.context=k,d=n):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),d=!1)}return Af(a,b,c,d,f,e)}function Af(a,b,c,d,e,f){qi(a,b);var g=0!==(b.flags&128);if(!d&&!g)return e&&vh(b,c,!1), +Qa(a,b,f);d=b.stateNode;tk.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?null:d.render();b.flags|=1;null!==a&&g?(b.child=Vb(b,a.child,null,f),b.child=Vb(b,null,h,f)):aa(a,b,h,f);b.memoizedState=d.state;e&&vh(b,c,!0);return b.child}function si(a){var b=a.stateNode;b.pendingContext?th(a,b.pendingContext,b.pendingContext!==b.context):b.context&&th(a,b.context,!1);gf(a,b.containerInfo)}function ti(a,b,c,d,e){Qb();Ye(e);b.flags|=256;aa(a,b,c,d);return b.child}function Bf(a){return{baseLanes:a, +cachePool:null,transitions:null}}function ui(a,b,c){var d=b.pendingProps,e=F.current,f=!1,g=0!==(b.flags&128),h;(h=g)||(h=null!==a&&null===a.memoizedState?!1:0!==(e&2));if(h)f=!0,b.flags&=-129;else if(null===a||null!==a.memoizedState)e|=1;y(F,e&1);if(null===a){Xe(b);a=b.memoizedState;if(null!==a&&(a=a.dehydrated,null!==a))return 0===(b.mode&1)?b.lanes=1:"$!"===a.data?b.lanes=8:b.lanes=1073741824,null;g=d.children;a=d.fallback;return f?(d=b.mode,f=b.child,g={mode:"hidden",children:g},0===(d&1)&&null!== +f?(f.childLanes=0,f.pendingProps=g):f=Gd(g,d,0,null),a=sb(a,d,c,null),f.return=b,a.return=b,f.sibling=a,b.child=f,b.child.memoizedState=Bf(c),b.memoizedState=Cf,a):Df(b,g)}e=a.memoizedState;if(null!==e&&(h=e.dehydrated,null!==h))return uk(a,b,g,d,h,e,c);if(f){f=d.fallback;g=b.mode;e=a.child;h=e.sibling;var k={mode:"hidden",children:d.children};0===(g&1)&&b.child!==e?(d=b.child,d.childLanes=0,d.pendingProps=k,b.deletions=null):(d=eb(e,k),d.subtreeFlags=e.subtreeFlags&14680064);null!==h?f=eb(h,f):(f= +sb(f,g,c,null),f.flags|=2);f.return=b;d.return=b;d.sibling=f;b.child=d;d=f;f=b.child;g=a.child.memoizedState;g=null===g?Bf(c):{baseLanes:g.baseLanes|c,cachePool:null,transitions:g.transitions};f.memoizedState=g;f.childLanes=a.childLanes&~c;b.memoizedState=Cf;return d}f=a.child;a=f.sibling;d=eb(f,{mode:"visible",children:d.children});0===(b.mode&1)&&(d.lanes=c);d.return=b;d.sibling=null;null!==a&&(c=b.deletions,null===c?(b.deletions=[a],b.flags|=16):c.push(a));b.child=d;b.memoizedState=null;return d} +function Df(a,b,c){b=Gd({mode:"visible",children:b},a.mode,0,null);b.return=a;return a.child=b}function Hd(a,b,c,d){null!==d&&Ye(d);Vb(b,a.child,null,c);a=Df(b,b.pendingProps.children);a.flags|=2;b.memoizedState=null;return a}function uk(a,b,c,d,e,f,g){if(c){if(b.flags&256)return b.flags&=-257,d=vf(Error(m(422))),Hd(a,b,g,d);if(null!==b.memoizedState)return b.child=a.child,b.flags|=128,null;f=d.fallback;e=b.mode;d=Gd({mode:"visible",children:d.children},e,0,null);f=sb(f,e,g,null);f.flags|=2;d.return= +b;f.return=b;d.sibling=f;b.child=d;0!==(b.mode&1)&&Vb(b,a.child,null,g);b.child.memoizedState=Bf(g);b.memoizedState=Cf;return f}if(0===(b.mode&1))return Hd(a,b,g,null);if("$!"===e.data){d=e.nextSibling&&e.nextSibling.dataset;if(d)var h=d.dgst;d=h;f=Error(m(419));d=vf(f,d,void 0);return Hd(a,b,g,d)}h=0!==(g&a.childLanes);if(ha||h){d=O;if(null!==d){switch(g&-g){case 4:e=2;break;case 16:e=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:e= +32;break;case 536870912:e=268435456;break;default:e=0}e=0!==(e&(d.suspendedLanes|g))?0:e;0!==e&&e!==f.retryLane&&(f.retryLane=e,Oa(a,e),xa(d,a,e,-1))}Ef();d=vf(Error(m(421)));return Hd(a,b,g,d)}if("$?"===e.data)return b.flags|=128,b.child=a.child,b=vk.bind(null,a),e._reactRetry=b,null;a=f.treeContext;fa=Ka(e.nextSibling);la=b;D=!0;wa=null;null!==a&&(na[oa++]=Ma,na[oa++]=Na,na[oa++]=rb,Ma=a.id,Na=a.overflow,rb=b);b=Df(b,d.children);b.flags|=4096;return b}function vi(a,b,c){a.lanes|=b;var d=a.alternate; +null!==d&&(d.lanes|=b);df(a.return,b,c)}function Ff(a,b,c,d,e){var f=a.memoizedState;null===f?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailMode:e}:(f.isBackwards=b,f.rendering=null,f.renderingStartTime=0,f.last=d,f.tail=c,f.tailMode=e)}function wi(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;aa(a,b,d.children,c);d=F.current;if(0!==(d&2))d=d&1|2,b.flags|=128;else{if(null!==a&&0!==(a.flags&128))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&& +vi(a,c,b);else if(19===a.tag)vi(a,c,b);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(F,d);if(0===(b.mode&1))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===xd(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ff(b,!1,e,c,f);break;case "backwards":c= +null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===xd(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ff(b,!0,c,null,f);break;case "together":Ff(b,!1,null,null,void 0);break;default:b.memoizedState=null}return b.child}function Fd(a,b){0===(b.mode&1)&&null!==a&&(a.alternate=null,b.alternate=null,b.flags|=2)}function Qa(a,b,c){null!==a&&(b.dependencies=a.dependencies);ra|=b.lanes;if(0===(c&b.childLanes))return null;if(null!==a&&b.child!==a.child)throw Error(m(153));if(null!== +b.child){a=b.child;c=eb(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=eb(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}function wk(a,b,c){switch(b.tag){case 3:si(b);Qb();break;case 5:Ih(b);break;case 1:ea(b.type)&&ld(b);break;case 4:gf(b,b.stateNode.containerInfo);break;case 10:var d=b.type._context,e=b.memoizedProps.value;y(ud,d._currentValue);d._currentValue=e;break;case 13:d=b.memoizedState;if(null!==d){if(null!==d.dehydrated)return y(F,F.current& +1),b.flags|=128,null;if(0!==(c&b.child.childLanes))return ui(a,b,c);y(F,F.current&1);a=Qa(a,b,c);return null!==a?a.sibling:null}y(F,F.current&1);break;case 19:d=0!==(c&b.childLanes);if(0!==(a.flags&128)){if(d)return wi(a,b,c);b.flags|=128}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null,e.lastEffect=null);y(F,F.current);if(d)break;else return null;case 22:case 23:return b.lanes=0,pi(a,b,c)}return Qa(a,b,c)}function Dc(a,b){if(!D)switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!== +b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function W(a){var b=null!==a.alternate&&a.alternate.child===a.child,c=0,d=0;if(b)for(var e=a.child;null!==e;)c|=e.lanes|e.childLanes,d|=e.subtreeFlags&14680064,d|=e.flags&14680064,e.return=a,e=e.sibling;else for(e=a.child;null!==e;)c|=e.lanes|e.childLanes, +d|=e.subtreeFlags,d|=e.flags,e.return=a,e=e.sibling;a.subtreeFlags|=d;a.childLanes=c;return b}function xk(a,b,c){var d=b.pendingProps;Ve(b);switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return W(b),null;case 1:return ea(b.type)&&(v(S),v(J)),W(b),null;case 3:d=b.stateNode;Tb();v(S);v(J);jf();d.pendingContext&&(d.context=d.pendingContext,d.pendingContext=null);if(null===a||null===a.child)pd(b)?b.flags|=4:null===a||a.memoizedState.isDehydrated&&0===(b.flags& +256)||(b.flags|=1024,null!==wa&&(Gf(wa),wa=null));xi(a,b);W(b);return null;case 5:hf(b);var e=ub(xc.current);c=b.type;if(null!==a&&null!=b.stateNode)yk(a,b,c,d,e),a.ref!==b.ref&&(b.flags|=512,b.flags|=2097152);else{if(!d){if(null===b.stateNode)throw Error(m(166));W(b);return null}a=ub(Ea.current);if(pd(b)){d=b.stateNode;c=b.type;var f=b.memoizedProps;d[Da]=b;d[uc]=f;a=0!==(b.mode&1);switch(c){case "dialog":B("cancel",d);B("close",d);break;case "iframe":case "object":case "embed":B("load",d);break; +case "video":case "audio":for(e=0;e\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(c,{is:d.is}):(a=g.createElement(c),"select"===c&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,c);a[Da]=b;a[uc]=d;zk(a,b,!1,!1);b.stateNode=a;a:{g=qe(c,d);switch(c){case "dialog":B("cancel",a);B("close",a);e=d;break;case "iframe":case "object":case "embed":B("load",a);e=d;break; +case "video":case "audio":for(e=0;eHf&&(b.flags|=128,d=!0,Dc(f,!1),b.lanes=4194304)}else{if(!d)if(a=xd(g),null!==a){if(b.flags|=128,d=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.flags|=4),Dc(f,!0),null===f.tail&&"hidden"===f.tailMode&&!g.alternate&&!D)return W(b),null}else 2*P()-f.renderingStartTime>Hf&&1073741824!==c&&(b.flags|= +128,d=!0,Dc(f,!1),b.lanes=4194304);f.isBackwards?(g.sibling=b.child,b.child=g):(c=f.last,null!==c?c.sibling=g:b.child=g,f.last=g)}if(null!==f.tail)return b=f.tail,f.rendering=b,f.tail=b.sibling,f.renderingStartTime=P(),b.sibling=null,c=F.current,y(F,d?c&1|2:c&1),b;W(b);return null;case 22:case 23:return ba=Ga.current,v(Ga),d=null!==b.memoizedState,null!==a&&null!==a.memoizedState!==d&&(b.flags|=8192),d&&0!==(b.mode&1)?0!==(ba&1073741824)&&(W(b),b.subtreeFlags&6&&(b.flags|=8192)):W(b),null;case 24:return null; +case 25:return null}throw Error(m(156,b.tag));}function Bk(a,b,c){Ve(b);switch(b.tag){case 1:return ea(b.type)&&(v(S),v(J)),a=b.flags,a&65536?(b.flags=a&-65537|128,b):null;case 3:return Tb(),v(S),v(J),jf(),a=b.flags,0!==(a&65536)&&0===(a&128)?(b.flags=a&-65537|128,b):null;case 5:return hf(b),null;case 13:v(F);a=b.memoizedState;if(null!==a&&null!==a.dehydrated){if(null===b.alternate)throw Error(m(340));Qb()}a=b.flags;return a&65536?(b.flags=a&-65537|128,b):null;case 19:return v(F),null;case 4:return Tb(), +null;case 10:return cf(b.type._context),null;case 22:case 23:return ba=Ga.current,v(Ga),null;case 24:return null;default:return null}}function Wb(a,b){var c=a.ref;if(null!==c)if("function"===typeof c)try{c(null)}catch(d){G(a,b,d)}else c.current=null}function If(a,b,c){try{c()}catch(d){G(a,b,d)}}function Ck(a,b){Jf=Zc;a=ch();if(Ie(a)){if("selectionStart"in a)var c={start:a.selectionStart,end:a.selectionEnd};else a:{c=(c=a.ownerDocument)&&c.defaultView||window;var d=c.getSelection&&c.getSelection(); +if(d&&0!==d.rangeCount){c=d.anchorNode;var e=d.anchorOffset,f=d.focusNode;d=d.focusOffset;try{c.nodeType,f.nodeType}catch(M){c=null;break a}var g=0,h=-1,k=-1,n=0,q=0,u=a,r=null;b:for(;;){for(var p;;){u!==c||0!==e&&3!==u.nodeType||(h=g+e);u!==f||0!==d&&3!==u.nodeType||(k=g+d);3===u.nodeType&&(g+=u.nodeValue.length);if(null===(p=u.firstChild))break;r=u;u=p}for(;;){if(u===a)break b;r===c&&++n===e&&(h=g);r===f&&++q===d&&(k=g);if(null!==(p=u.nextSibling))break;u=r;r=u.parentNode}u=p}c=-1===h||-1===k?null: +{start:h,end:k}}else c=null}c=c||{start:0,end:0}}else c=null;Kf={focusedElem:a,selectionRange:c};Zc=!1;for(l=b;null!==l;)if(b=l,a=b.child,0!==(b.subtreeFlags&1028)&&null!==a)a.return=b,l=a;else for(;null!==l;){b=l;try{var x=b.alternate;if(0!==(b.flags&1024))switch(b.tag){case 0:case 11:case 15:break;case 1:if(null!==x){var v=x.memoizedProps,z=x.memoizedState,w=b.stateNode,A=w.getSnapshotBeforeUpdate(b.elementType===b.type?v:ya(b.type,v),z);w.__reactInternalSnapshotBeforeUpdate=A}break;case 3:var t= +b.stateNode.containerInfo;1===t.nodeType?t.textContent="":9===t.nodeType&&t.documentElement&&t.removeChild(t.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(m(163));}}catch(M){G(b,b.return,M)}a=b.sibling;if(null!==a){a.return=b.return;l=a;break}l=b.return}x=zi;zi=!1;return x}function Gc(a,b,c){var d=b.updateQueue;d=null!==d?d.lastEffect:null;if(null!==d){var e=d=d.next;do{if((e.tag&a)===a){var f=e.destroy;e.destroy=void 0;void 0!==f&&If(b,c,f)}e=e.next}while(e!==d)}} +function Id(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function Lf(a){var b=a.ref;if(null!==b){var c=a.stateNode;switch(a.tag){case 5:a=c;break;default:a=c}"function"===typeof b?b(a):b.current=a}}function Ai(a){var b=a.alternate;null!==b&&(a.alternate=null,Ai(b));a.child=null;a.deletions=null;a.sibling=null;5===a.tag&&(b=a.stateNode,null!==b&&(delete b[Da],delete b[uc],delete b[Me],delete b[Dk], +delete b[Ek]));a.stateNode=null;a.return=null;a.dependencies=null;a.memoizedProps=null;a.memoizedState=null;a.pendingProps=null;a.stateNode=null;a.updateQueue=null}function Bi(a){return 5===a.tag||3===a.tag||4===a.tag}function Ci(a){a:for(;;){for(;null===a.sibling;){if(null===a.return||Bi(a.return))return null;a=a.return}a.sibling.return=a.return;for(a=a.sibling;5!==a.tag&&6!==a.tag&&18!==a.tag;){if(a.flags&2)continue a;if(null===a.child||4===a.tag)continue a;else a.child.return=a,a=a.child}if(!(a.flags& +2))return a.stateNode}}function Mf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=kd));else if(4!==d&&(a=a.child,null!==a))for(Mf(a,b,c),a=a.sibling;null!==a;)Mf(a,b,c),a=a.sibling}function Nf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?c.insertBefore(a,b):c.appendChild(a); +else if(4!==d&&(a=a.child,null!==a))for(Nf(a,b,c),a=a.sibling;null!==a;)Nf(a,b,c),a=a.sibling}function jb(a,b,c){for(c=c.child;null!==c;)Di(a,b,c),c=c.sibling}function Di(a,b,c){if(Ca&&"function"===typeof Ca.onCommitFiberUnmount)try{Ca.onCommitFiberUnmount(Uc,c)}catch(h){}switch(c.tag){case 5:X||Wb(c,b);case 6:var d=T,e=za;T=null;jb(a,b,c);T=d;za=e;null!==T&&(za?(a=T,c=c.stateNode,8===a.nodeType?a.parentNode.removeChild(c):a.removeChild(c)):T.removeChild(c.stateNode));break;case 18:null!==T&&(za? +(a=T,c=c.stateNode,8===a.nodeType?Re(a.parentNode,c):1===a.nodeType&&Re(a,c),nc(a)):Re(T,c.stateNode));break;case 4:d=T;e=za;T=c.stateNode.containerInfo;za=!0;jb(a,b,c);T=d;za=e;break;case 0:case 11:case 14:case 15:if(!X&&(d=c.updateQueue,null!==d&&(d=d.lastEffect,null!==d))){e=d=d.next;do{var f=e,g=f.destroy;f=f.tag;void 0!==g&&(0!==(f&2)?If(c,b,g):0!==(f&4)&&If(c,b,g));e=e.next}while(e!==d)}jb(a,b,c);break;case 1:if(!X&&(Wb(c,b),d=c.stateNode,"function"===typeof d.componentWillUnmount))try{d.props= +c.memoizedProps,d.state=c.memoizedState,d.componentWillUnmount()}catch(h){G(c,b,h)}jb(a,b,c);break;case 21:jb(a,b,c);break;case 22:c.mode&1?(X=(d=X)||null!==c.memoizedState,jb(a,b,c),X=d):jb(a,b,c);break;default:jb(a,b,c)}}function Ei(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode=new Fk);b.forEach(function(b){var d=Gk.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}function Aa(a,b,c){c=b.deletions;if(null!==c)for(var d=0;de&&(e=g);d&=~f}d=e;d=P()-d;d=(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*Mk(d/1960))-d;if(10a?16:a;if(null===lb)var d=!1;else{a=lb;lb=null;Qd=0;if(0!==(p&6))throw Error(m(331));var e=p;p|=4;for(l=a.current;null!==l;){var f=l,g=f.child;if(0!==(l.flags&16)){var h=f.deletions;if(null!==h){for(var k=0;kP()-Of?wb(a,0):Sf|=c);ia(a,b)}function Ti(a,b){0===b&&(0===(a.mode&1)?b=1:(b=Rd,Rd<<=1,0===(Rd&130023424)&&(Rd=4194304)));var c=Z();a=Oa(a,b);null!==a&&(ic(a,b,c),ia(a,c))}function vk(a){var b=a.memoizedState,c=0;null!==b&&(c=b.retryLane);Ti(a,c)}function Gk(a,b){var c=0;switch(a.tag){case 13:var d=a.stateNode;var e=a.memoizedState;null!==e&&(c=e.retryLane); +break;case 19:d=a.stateNode;break;default:throw Error(m(314));}null!==d&&d.delete(b);Ti(a,c)}function Mi(a,b){return xh(a,b)}function Tk(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.subtreeFlags=this.flags=0;this.deletions=null;this.childLanes=this.lanes=0;this.alternate=null}function yf(a){a= +a.prototype;return!(!a||!a.isReactComponent)}function Uk(a){if("function"===typeof a)return yf(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===ie)return 11;if(a===je)return 14}return 2}function eb(a,b){var c=a.alternate;null===c?(c=pa(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.type=a.type,c.flags=0,c.subtreeFlags=0,c.deletions=null);c.flags=a.flags&14680064;c.childLanes=a.childLanes;c.lanes=a.lanes;c.child= +a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{lanes:b.lanes,firstContext:b.firstContext};c.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}function rd(a,b,c,d,e,f){var g=2;d=a;if("function"===typeof a)yf(a)&&(g=1);else if("string"===typeof a)g=5;else a:switch(a){case Bb:return sb(c.children,e,f,b);case fe:g=8;e|=8;break;case ee:return a=pa(12,c,b,e|2),a.elementType=ee,a.lanes=f,a;case ge:return a= +pa(13,c,b,e),a.elementType=ge,a.lanes=f,a;case he:return a=pa(19,c,b,e),a.elementType=he,a.lanes=f,a;case Ui:return Gd(c,e,f,b);default:if("object"===typeof a&&null!==a)switch(a.$$typeof){case hg:g=10;break a;case gg:g=9;break a;case ie:g=11;break a;case je:g=14;break a;case Ta:g=16;d=null;break a}throw Error(m(130,null==a?a:typeof a,""));}b=pa(g,c,b,e);b.elementType=a;b.type=d;b.lanes=f;return b}function sb(a,b,c,d){a=pa(7,a,d,b);a.lanes=c;return a}function Gd(a,b,c,d){a=pa(22,a,d,b);a.elementType= +Ui;a.lanes=c;a.stateNode={isHidden:!1};return a}function Ze(a,b,c){a=pa(6,a,null,b);a.lanes=c;return a}function $e(a,b,c){b=pa(4,null!==a.children?a.children:[],a.key,b);b.lanes=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}function Vk(a,b,c,d,e){this.tag=b;this.containerInfo=a;this.finishedWork=this.pingCache=this.current=this.pendingChildren=null;this.timeoutHandle=-1;this.callbackNode=this.pendingContext=this.context=null;this.callbackPriority= +0;this.eventTimes=we(0);this.expirationTimes=we(-1);this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0;this.entanglements=we(0);this.identifierPrefix=d;this.onRecoverableError=e;this.mutableSourceEagerHydrationData=null}function Vf(a,b,c,d,e,f,g,h,k,l){a=new Vk(a,b,c,h,k);1===b?(b=1,!0===f&&(b|=8)):b=0;f=pa(3,null,null,b);a.current=f;f.stateNode=a;f.memoizedState={element:d,isDehydrated:c,cache:null,transitions:null, +pendingSuspenseBoundaries:null};ff(f);return a}function Wk(a,b,c){var d=3