GitHub PullRequestReviewCommentEvent at 2014-07-10 09:29:01
Repository: scala-js
Actor: sjrd
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/scala-js/scala-js/pulls/comments/14756372","id":14756372,"diff_hunk":"@@ -0,0 +1,599 @@\n+/*                     __                                               *\\\n+**     ________ ___   / /  ___      __ ____  Scala.js tools             **\n+**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013-2014, LAMP/EPFL   **\n+**  __\\ \\/ /__/ __ |/ /__/ __ |/_// /_\\ \\    http://scala-js.org/       **\n+** /____/\\___/_/ |_/____/_/ | |__/ /____/                               **\n+**                          |/____/                                     **\n+\\*                                                                      */\n+\n+\n+package scala.scalajs.tools.optimizer\n+\n+import scala.annotation.{switch, tailrec}\n+\n+import scala.collection.mutable\n+\n+import scala.scalajs.ir._\n+import Infos.OptimizerHints\n+import Trees._\n+import Types._\n+\n+import scala.scalajs.tools.logging._\n+\n+/** Incremental optimizer.\n+ *  An incremental optimizer consumes the reachability analysis produced by\n+ *  an [[Analyzer]], as well as trees for classes, trait impls, etc., and\n+ *  optimizes them in an incremental way.\n+ *  It maintains state between runs to do a minimal amount of work on every\n+ *  run, based on detecting what parts of the program must be re-optimized,\n+ *  and keeping optimized results from previous runs for the rest.\n+ */\n+class IncOptimizer {\n+  import IncOptimizer._\n+\n+  private var logger: Logger = _\n+\n+  /** Are we in batch mode? I.e., are we running from scratch?\n+   *  Various parts of the algorithm can be skipped entirely when running in\n+   *  batch mode.\n+   */\n+  private var batchMode: Boolean = false\n+\n+  private var objectClass: Class = _\n+  private val classes = mutable.Map.empty[String, Class]\n+  private val traitImpls = mutable.Map.empty[String, TraitImpl]\n+  private val _interfaces = mutable.Map.empty[String, InterfaceType]\n+\n+  def findTraitImpl(encodedName: String): TraitImpl = traitImpls(encodedName)\n+  def findClass(encodedName: String): Class = classes(encodedName)\n+\n+  private def getInterface(encodedName: String): InterfaceType =\n+    _interfaces.getOrElseUpdate(encodedName, new InterfaceType(encodedName))\n+\n+  private var methodsToProcess: mutable.Set[MethodImpl] = mutable.Set.empty\n+\n+  type GetClassTreeIfChanged =\n+    (String, Option[String]) => Option[(ClassDef, Option[String])]\n+\n+  private def withLogger[A](logger: Logger)(body: => A): A = {\n+    assert(this.logger == null)\n+    this.logger = logger\n+    try body\n+    finally this.logger = null\n+  }\n+\n+  /** Update the incremental analyzer with a new run. */\n+  def update(analyzer: Analyzer,\n+      getClassTreeIfChanged: GetClassTreeIfChanged,\n+      logger: Logger): Unit = withLogger(logger) {\n+\n+    batchMode = objectClass == null\n+    logger.debug(s\"Optimizer batch mode: $batchMode\")\n+\n+    logTime(logger, \"Incremental part of inc. optimizer\") {\n+      updateAndTagEverything(analyzer, getClassTreeIfChanged)\n+    }\n+\n+    logTime(logger, \"Optimizer part of inc. optimizer\") {\n+      processAllTaggedMethods()\n+    }\n+  }\n+\n+  /** Incremental part: update state and detect what needs to be re-optimized.\n+   */\n+  private def updateAndTagEverything(analyzer: Analyzer,\n+      getClassTreeIfChanged: GetClassTreeIfChanged): Unit = {\n+\n+    val neededClasses = mutable.Map.empty[String, analyzer.ClassInfo]\n+    val neededTraitImpls = mutable.Map.empty[String, analyzer.ClassInfo]\n+    for {\n+      classInfo <- analyzer.classInfos.values\n+      if classInfo.isNeededAtAll\n+    } {\n+      if (classInfo.hasInstantiation && classInfo.isAnySubclassInstantiated)\n+        neededClasses += classInfo.encodedName -> classInfo\n+      else if (classInfo.isImplClass)\n+        neededTraitImpls += classInfo.encodedName -> classInfo\n+    }\n+\n+    /* Remove deleted trait impls, and update existing trait impls.\n+     * We don't even have to notify callers in case of additions or removals\n+     * because callers have got to be invalidated by themselves.\n+     * Only changed methods need to trigger notifications.\n+     *\n+     * Non-batch mode only.\n+     */\n+    assert(!batchMode || traitImpls.isEmpty)\n+    if (!batchMode) {\n+      traitImpls retain { (traitImplName, traitImpl) =>\n+        neededTraitImpls.remove(traitImplName).fold {\n+          /* Deleted trait impl. Mark all its methods as deleted, and remove it\n+           * from known trait impls.\n+           */\n+          traitImpl.methods.values.foreach(_.delete())\n+          false\n+        } { traitImplInfo =>\n+          /* Existing trait impl. Update it. */\n+          val (added, changed, removed) =\n+            traitImpl.updateMethodsWith(traitImplInfo, getClassTreeIfChanged)\n+          for (method <- changed)\n+            traitImpl.interface.tagStaticCallersOf(method)\n+          true\n+        }\n+      }\n+    }\n+\n+    /* Add new trait impls.\n+     * Easy, we don't have to notify anyone.\n+     */\n+    for (traitImplInfo <- neededTraitImpls.values) {\n+      val traitImpl = new TraitImpl(traitImplInfo.encodedName)\n+      traitImpls += traitImpl.encodedName -> traitImpl\n+      traitImpl.updateMethodsWith(traitImplInfo, getClassTreeIfChanged)\n+    }\n+\n+    if (!batchMode) {\n+      /* Class removals:\n+       * * If a class is deleted or moved, delete its entire subtree (because\n+       *   all its descendants must also be deleted or moved).\n+       * * If an existing class was instantiated but is no more, notify callers\n+       *   of its methods.\n+       *\n+       * Non-batch mode only.\n+       */\n+      def walkClassesForDeletion(cls: Class): Boolean = {\n+        def sameSuperClass(info: analyzer.ClassInfo): Boolean =\n+          if (info.superClass == null) cls.superClass.isEmpty\n+          else cls.superClass.exists(_.encodedName == info.superClass.encodedName)\n+        neededClasses.get(cls.encodedName) match {\n+          case Some(classInfo) if sameSuperClass(classInfo) =>\n+            // Class still exists. Recurse.\n+            cls.subclasses = cls.subclasses.filter(walkClassesForDeletion)\n+            if (cls.isInstantiated && !classInfo.isInstantiated)\n+              cls.notInstantiatedAnymore()\n+            true\n+          case _ =>\n+            // Class does not exist or has been moved. Delete the entire subtree.\n+            cls.deleteSubtree()\n+            false\n+        }\n+      }\n+      val objectClassStillExists = walkClassesForDeletion(objectClass)\n+      assert(objectClassStillExists, \"Uh oh, java.lang.Object was deleted!\")\n+\n+      /* Class changes:\n+       * * Delete removed methods, update existing ones, add new ones\n+       * * Update the list of ancestors\n+       * * Class newly instantiated\n+       *\n+       * Non-batch mode only.\n+       */\n+      objectClass.walkForChanges(\n+          neededClasses.remove(_).get,\n+          getClassTreeIfChanged,\n+          Set.empty)\n+    }\n+\n+    /* Class additions:\n+     * * Add new classes (including those that have moved from elsewhere).\n+     * In batch mode, we avoid doing notifications.\n+     */\n+    for (classInfo <- neededClasses.values.toList.sortBy(_.ancestorCount)) {\n+      val superClassInfo = classInfo.superClass\n+      val cls = new Class(\n+          Option(superClassInfo).map(info => classes(info.encodedName)),\n+          classInfo.encodedName)\n+      cls.updateMethodsWith(classInfo, getClassTreeIfChanged)\n+      cls.interfaces =\n+        classInfo.ancestors.map(info => getInterface(info.encodedName)).toSet","path":"tools/src/main/scala/scala/scalajs/tools/optimizer/IncOptimizer.scala","position":188,"original_position":188,"commit_id":"922f918887f833444a4e3d991d8f7164ccf9ea29","original_commit_id":"922f918887f833444a4e3d991d8f7164ccf9ea29","user":{"login":"sjrd","id":535934,"avatar_url":"https://avatars.githubusercontent.com/u/535934?","gravatar_id":"bc554c7355c8476dd63375b314b1bd97","url":"https://api.github.com/users/sjrd","html_url":"https://github.com/sjrd","followers_url":"https://api.github.com/users/sjrd/followers","following_url":"https://api.github.com/users/sjrd/following{/other_user}","gists_url":"https://api.github.com/users/sjrd/gists{/gist_id}","starred_url":"https://api.github.com/users/sjrd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sjrd/subscriptions","organizations_url":"https://api.github.com/users/sjrd/orgs","repos_url":"https://api.github.com/users/sjrd/repos","events_url":"https://api.github.com/users/sjrd/events{/privacy}","received_events_url":"https://api.github.com/users/sjrd/received_events","type":"User","site_admin":false},"body":"I don't put things in the constructor that can be changed. Too hard to track.","created_at":"2014-07-10T09:29:01Z","updated_at":"2014-07-10T09:29:01Z","html_url":"https://github.com/scala-js/scala-js/pull/810#discussion_r14756372","pull_request_url":"https://api.github.com/repos/scala-js/scala-js/pulls/810","_links":{"self":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/comments/14756372"},"html":{"href":"https://github.com/scala-js/scala-js/pull/810#discussion_r14756372"},"pull_request":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/810"}}},"pull_request":{"url":"https://api.github.com/repos/scala-js/scala-js/pulls/810","id":18021388,"html_url":"https://github.com/scala-js/scala-js/pull/810","diff_url":"https://github.com/scala-js/scala-js/pull/810.diff","patch_url":"https://github.com/scala-js/scala-js/pull/810.patch","issue_url":"https://api.github.com/repos/scala-js/scala-js/issues/810","number":810,"state":"open","title":"DO NOT MERGE Incremental inliner","user":{"login":"sjrd","id":535934,"avatar_url":"https://avatars.githubusercontent.com/u/535934?","gravatar_id":"bc554c7355c8476dd63375b314b1bd97","url":"https://api.github.com/users/sjrd","html_url":"https://github.com/sjrd","followers_url":"https://api.github.com/users/sjrd/followers","following_url":"https://api.github.com/users/sjrd/following{/other_user}","gists_url":"https://api.github.com/users/sjrd/gists{/gist_id}","starred_url":"https://api.github.com/users/sjrd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sjrd/subscriptions","organizations_url":"https://api.github.com/users/sjrd/orgs","repos_url":"https://api.github.com/users/sjrd/repos","events_url":"https://api.github.com/users/sjrd/events{/privacy}","received_events_url":"https://api.github.com/users/sjrd/received_events","type":"User","site_admin":false},"body":"**Do not merge this PR yet**, it is obviously in a very rough state.\r\n\r\nBut now the algorithm should be complete enough that things won't change much anymore, except cleanups. So I'm posting it for the eager interested reader.","created_at":"2014-07-06T20:26:20Z","updated_at":"2014-07-10T09:29:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cd71586e6f50610dd3a02335ff64fbc55bbac666","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scala-js/scala-js/pulls/810/commits","review_comments_url":"https://api.github.com/repos/scala-js/scala-js/pulls/810/comments","review_comment_url":"https://api.github.com/repos/scala-js/scala-js/pulls/comments/{number}","comments_url":"https://api.github.com/repos/scala-js/scala-js/issues/810/comments","statuses_url":"https://api.github.com/repos/scala-js/scala-js/statuses/922f918887f833444a4e3d991d8f7164ccf9ea29","head":{"label":"sjrd:inliner","ref":"inliner","sha":"922f918887f833444a4e3d991d8f7164ccf9ea29","user":{"login":"sjrd","id":535934,"avatar_url":"https://avatars.githubusercontent.com/u/535934?","gravatar_id":"bc554c7355c8476dd63375b314b1bd97","url":"https://api.github.com/users/sjrd","html_url":"https://github.com/sjrd","followers_url":"https://api.github.com/users/sjrd/followers","following_url":"https://api.github.com/users/sjrd/following{/other_user}","gists_url":"https://api.github.com/users/sjrd/gists{/gist_id}","starred_url":"https://api.github.com/users/sjrd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sjrd/subscriptions","organizations_url":"https://api.github.com/users/sjrd/orgs","repos_url":"https://api.github.com/users/sjrd/repos","events_url":"https://api.github.com/users/sjrd/events{/privacy}","received_events_url":"https://api.github.com/users/sjrd/received_events","type":"User","site_admin":false},"repo":{"id":8660284,"name":"scala-js","full_name":"sjrd/scala-js","owner":{"login":"sjrd","id":535934,"avatar_url":"https://avatars.githubusercontent.com/u/535934?","gravatar_id":"bc554c7355c8476dd63375b314b1bd97","url":"https://api.github.com/users/sjrd","html_url":"https://github.com/sjrd","followers_url":"https://api.github.com/users/sjrd/followers","following_url":"https://api.github.com/users/sjrd/following{/other_user}","gists_url":"https://api.github.com/users/sjrd/gists{/gist_id}","starred_url":"https://api.github.com/users/sjrd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sjrd/subscriptions","organizations_url":"https://api.github.com/users/sjrd/orgs","repos_url":"https://api.github.com/users/sjrd/repos","events_url":"https://api.github.com/users/sjrd/events{/privacy}","received_events_url":"https://api.github.com/users/sjrd/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sjrd/scala-js","description":"Scala.js, a Scala to JavaScript compiler","fork":true,"url":"https://api.github.com/repos/sjrd/scala-js","forks_url":"https://api.github.com/repos/sjrd/scala-js/forks","keys_url":"https://api.github.com/repos/sjrd/scala-js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sjrd/scala-js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sjrd/scala-js/teams","hooks_url":"https://api.github.com/repos/sjrd/scala-js/hooks","issue_events_url":"https://api.github.com/repos/sjrd/scala-js/issues/events{/number}","events_url":"https://api.github.com/repos/sjrd/scala-js/events","assignees_url":"https://api.github.com/repos/sjrd/scala-js/assignees{/user}","branches_url":"https://api.github.com/repos/sjrd/scala-js/branches{/branch}","tags_url":"https://api.github.com/repos/sjrd/scala-js/tags","blobs_url":"https://api.github.com/repos/sjrd/scala-js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sjrd/scala-js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sjrd/scala-js/git/refs{/sha}","trees_url":"https://api.github.com/repos/sjrd/scala-js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sjrd/scala-js/statuses/{sha}","languages_url":"https://api.github.com/repos/sjrd/scala-js/languages","stargazers_url":"https://api.github.com/repos/sjrd/scala-js/stargazers","contributors_url":"https://api.github.com/repos/sjrd/scala-js/contributors","subscribers_url":"https://api.github.com/repos/sjrd/scala-js/subscribers","subscription_url":"https://api.github.com/repos/sjrd/scala-js/subscription","commits_url":"https://api.github.com/repos/sjrd/scala-js/commits{/sha}","git_commits_url":"https://api.github.com/repos/sjrd/scala-js/git/commits{/sha}","comments_url":"https://api.github.com/repos/sjrd/scala-js/comments{/number}","issue_comment_url":"https://api.github.com/repos/sjrd/scala-js/issues/comments/{number}","contents_url":"https://api.github.com/repos/sjrd/scala-js/contents/{+path}","compare_url":"https://api.github.com/repos/sjrd/scala-js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sjrd/scala-js/merges","archive_url":"https://api.github.com/repos/sjrd/scala-js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sjrd/scala-js/downloads","issues_url":"https://api.github.com/repos/sjrd/scala-js/issues{/number}","pulls_url":"https://api.github.com/repos/sjrd/scala-js/pulls{/number}","milestones_url":"https://api.github.com/repos/sjrd/scala-js/milestones{/number}","notifications_url":"https://api.github.com/repos/sjrd/scala-js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sjrd/scala-js/labels{/name}","releases_url":"https://api.github.com/repos/sjrd/scala-js/releases{/id}","created_at":"2013-03-08T21:32:59Z","updated_at":"2014-06-25T11:50:28Z","pushed_at":"2014-07-10T07:53:29Z","git_url":"git://github.com/sjrd/scala-js.git","ssh_url":"git@github.com:sjrd/scala-js.git","clone_url":"https://github.com/sjrd/scala-js.git","svn_url":"https://github.com/sjrd/scala-js","homepage":"http://www.scala-js.org/","size":9929,"stargazers_count":11,"watchers_count":11,"language":"Scala","has_issues":false,"has_downloads":true,"has_wiki":true,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":11,"default_branch":"master"}},"base":{"label":"scala-js:master","ref":"master","sha":"ca8d339aa828130870a1ba7c630d125aa0fe733a","user":{"login":"scala-js","id":6039724,"avatar_url":"https://avatars.githubusercontent.com/u/6039724?","gravatar_id":"cd2aba324ee144fbe4066e0e2ee9966a","url":"https://api.github.com/users/scala-js","html_url":"https://github.com/scala-js","followers_url":"https://api.github.com/users/scala-js/followers","following_url":"https://api.github.com/users/scala-js/following{/other_user}","gists_url":"https://api.github.com/users/scala-js/gists{/gist_id}","starred_url":"https://api.github.com/users/scala-js/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scala-js/subscriptions","organizations_url":"https://api.github.com/users/scala-js/orgs","repos_url":"https://api.github.com/users/scala-js/repos","events_url":"https://api.github.com/users/scala-js/events{/privacy}","received_events_url":"https://api.github.com/users/scala-js/received_events","type":"Organization","site_admin":false},"repo":{"id":8272315,"name":"scala-js","full_name":"scala-js/scala-js","owner":{"login":"scala-js","id":6039724,"avatar_url":"https://avatars.githubusercontent.com/u/6039724?","gravatar_id":"cd2aba324ee144fbe4066e0e2ee9966a","url":"https://api.github.com/users/scala-js","html_url":"https://github.com/scala-js","followers_url":"https://api.github.com/users/scala-js/followers","following_url":"https://api.github.com/users/scala-js/following{/other_user}","gists_url":"https://api.github.com/users/scala-js/gists{/gist_id}","starred_url":"https://api.github.com/users/scala-js/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scala-js/subscriptions","organizations_url":"https://api.github.com/users/scala-js/orgs","repos_url":"https://api.github.com/users/scala-js/repos","events_url":"https://api.github.com/users/scala-js/events{/privacy}","received_events_url":"https://api.github.com/users/scala-js/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scala-js/scala-js","description":"Scala.js, a Scala to JavaScript compiler","fork":false,"url":"https://api.github.com/repos/scala-js/scala-js","forks_url":"https://api.github.com/repos/scala-js/scala-js/forks","keys_url":"https://api.github.com/repos/scala-js/scala-js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scala-js/scala-js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scala-js/scala-js/teams","hooks_url":"https://api.github.com/repos/scala-js/scala-js/hooks","issue_events_url":"https://api.github.com/repos/scala-js/scala-js/issues/events{/number}","events_url":"https://api.github.com/repos/scala-js/scala-js/events","assignees_url":"https://api.github.com/repos/scala-js/scala-js/assignees{/user}","branches_url":"https://api.github.com/repos/scala-js/scala-js/branches{/branch}","tags_url":"https://api.github.com/repos/scala-js/scala-js/tags","blobs_url":"https://api.github.com/repos/scala-js/scala-js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scala-js/scala-js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scala-js/scala-js/git/refs{/sha}","trees_url":"https://api.github.com/repos/scala-js/scala-js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scala-js/scala-js/statuses/{sha}","languages_url":"https://api.github.com/repos/scala-js/scala-js/languages","stargazers_url":"https://api.github.com/repos/scala-js/scala-js/stargazers","contributors_url":"https://api.github.com/repos/scala-js/scala-js/contributors","subscribers_url":"https://api.github.com/repos/scala-js/scala-js/subscribers","subscription_url":"https://api.github.com/repos/scala-js/scala-js/subscription","commits_url":"https://api.github.com/repos/scala-js/scala-js/commits{/sha}","git_commits_url":"https://api.github.com/repos/scala-js/scala-js/git/commits{/sha}","comments_url":"https://api.github.com/repos/scala-js/scala-js/comments{/number}","issue_comment_url":"https://api.github.com/repos/scala-js/scala-js/issues/comments/{number}","contents_url":"https://api.github.com/repos/scala-js/scala-js/contents/{+path}","compare_url":"https://api.github.com/repos/scala-js/scala-js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scala-js/scala-js/merges","archive_url":"https://api.github.com/repos/scala-js/scala-js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scala-js/scala-js/downloads","issues_url":"https://api.github.com/repos/scala-js/scala-js/issues{/number}","pulls_url":"https://api.github.com/repos/scala-js/scala-js/pulls{/number}","milestones_url":"https://api.github.com/repos/scala-js/scala-js/milestones{/number}","notifications_url":"https://api.github.com/repos/scala-js/scala-js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scala-js/scala-js/labels{/name}","releases_url":"https://api.github.com/repos/scala-js/scala-js/releases{/id}","created_at":"2013-02-18T16:33:55Z","updated_at":"2014-07-09T17:28:40Z","pushed_at":"2014-07-10T05:49:20Z","git_url":"git://github.com/scala-js/scala-js.git","ssh_url":"git@github.com:scala-js/scala-js.git","clone_url":"https://github.com/scala-js/scala-js.git","svn_url":"https://github.com/scala-js/scala-js","homepage":"http://www.scala-js.org/","size":12199,"stargazers_count":1185,"watchers_count":1185,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"forks_count":80,"mirror_url":null,"open_issues_count":26,"forks":80,"open_issues":26,"watchers":1185,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/810"},"html":{"href":"https://github.com/scala-js/scala-js/pull/810"},"issue":{"href":"https://api.github.com/repos/scala-js/scala-js/issues/810"},"comments":{"href":"https://api.github.com/repos/scala-js/scala-js/issues/810/comments"},"review_comments":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/810/comments"},"review_comment":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/scala-js/scala-js/pulls/810/commits"},"statuses":{"href":"https://api.github.com/repos/scala-js/scala-js/statuses/922f918887f833444a4e3d991d8f7164ccf9ea29"}}}}